This commit is contained in:
Love 2024-09-04 21:54:32 +02:00
parent cc2ce40642
commit 226bc2b340

View File

@ -12,6 +12,8 @@
const char *const FORMAT_UNDERSCORE = "\033[4m"; const char *const FORMAT_UNDERSCORE = "\033[4m";
const char *const END_FORMATTING = "\033[0m"; const char *const END_FORMATTING = "\033[0m";
const int ANIMATION_SLEEP_MS = 100, ANIMATION_N_GENERATIONS = 20;
void printWelcome() { void printWelcome() {
std::cout << "Welcome to the TDDD86 Game of Life,\n" std::cout << "Welcome to the TDDD86 Game of Life,\n"
<< "a simulation of the lifecycle of a bacteria colony.\n" << "a simulation of the lifecycle of a bacteria colony.\n"
@ -52,17 +54,19 @@ std::string fileRead(const char *filePath) {
return content; return content;
} }
enum UserAction { NO_ACTION, ACTION, TICK, QUIT }; enum UserAction { ANIMATE, TICK, QUIT };
UserAction askUserForAction() { UserAction askUserForANIMATE() {
std::string userInput = input("a)nimate, t)ick, q)uit? "); while (true) {
if (userInput == "a") std::string userInput = input("a)nimate, t)ick, q)uit? ");
return UserAction::ACTION; if (userInput == "a")
else if (userInput == "t") return UserAction::ANIMATE;
return UserAction::TICK; else if (userInput == "t")
else if (userInput == "q") return UserAction::TICK;
return UserAction::QUIT; else if (userInput == "q")
return UserAction::NO_ACTION; return UserAction::QUIT;
std::cout << "The input '" << userInput << "' is an invalid option!\n";
}
} }
std::vector<std::string> split(const std::string &s, const char at) { std::vector<std::string> split(const std::string &s, const char at) {
@ -81,6 +85,75 @@ std::vector<std::string> split(const std::string &s, const char at) {
enum Cell { ALIVE, DEAD }; enum Cell { ALIVE, DEAD };
void gridPrint(Grid<Cell> &grid) {
std::stringstream ss;
for (int idxRow = 0; idxRow < grid.numRows(); idxRow++) {
for (int idxCol = 0; idxCol < grid.numCols(); idxCol++) {
char sign = grid.get(idxRow, idxCol) == Cell::ALIVE ? 'X' : '-';
ss << sign;
}
if (idxRow == grid.numRows() - 1)
ss << std::endl;
else
ss << '\n';
}
std::cout << ss.str();
}
int gridAliveNeighbors(Grid<Cell> &grid, int row, int col) {
int alive = 0;
for (int rowDelta = -1; rowDelta <= 1; rowDelta++) {
int checkRow = row + rowDelta;
// Skip out of bounds
if (checkRow < 0 || checkRow >= grid.numRows())
continue;
for (int colDelta = -1; colDelta <= 1; colDelta++) {
// Don't check the original piece
if (rowDelta == 0 && colDelta == 0)
continue;
int checkCol = col + colDelta;
// Skip out of bounds
if (checkCol < 0 || checkCol >= grid.numCols())
continue;
if (grid.get(checkCol, checkRow) == Cell::ALIVE)
alive++;
}
}
return alive;
}
/**
* Run one Game of Life tick.
* Cells (X) live and die by the following rules:
* - A cell with 1 or fewer neighbours dies.
* - Locations with 2 neighbours remain stable.
* - Locations with 3 neighbours will create life.
* - A cell with 4 or more neighbours dies.
*/
Grid<Cell> gridTick(Grid<Cell> &grid) {
Grid<Cell> nextGrid = grid;
for (int row = 0; row < grid.numRows(); row++) {
for (int col = 0; col < grid.numCols(); col++) {
int alive = gridAliveNeighbors(grid, row, col);
if (alive == 1)
nextGrid.set(row, col, Cell::DEAD);
else if (alive == 2)
nextGrid.set(row, col, grid.get(row, col));
else if (alive == 3)
nextGrid.set(row, col, Cell::ALIVE);
else if (alive == 4)
nextGrid.set(row, col, Cell::DEAD);
}
}
return nextGrid;
}
int main() { int main() {
printWelcome(); printWelcome();
@ -103,10 +176,10 @@ int main() {
std::cerr << "No column, row header, in the file!" << std::endl; std::cerr << "No column, row header, in the file!" << std::endl;
return 1; return 1;
} }
int columns, rows; int rows, columns;
try { try {
columns = std::stoi(lines[0]); rows = std::stoi(lines[0]);
rows = std::stoi(lines[1]); columns = std::stoi(lines[1]);
} catch (const std::invalid_argument &e) { } catch (const std::invalid_argument &e) {
std::cerr << "Column or row wasn't a number" << std::endl; std::cerr << "Column or row wasn't a number" << std::endl;
return 1; return 1;
@ -122,7 +195,7 @@ int main() {
Grid<Cell> grid(columns, rows); Grid<Cell> grid(columns, rows);
for (int x = 0; x < rows; x++) { for (int x = 0; x < rows; x++) {
std::string &row = lines[x]; std::string &row = lines[x + 2];
if (columns < row.size()) { if (columns < row.size()) {
std::cerr << "There's less columns, than described in the header!" std::cerr << "There's less columns, than described in the header!"
<< std::endl; << std::endl;
@ -146,6 +219,28 @@ int main() {
} }
} }
} }
gridPrint(grid);
UserAction ANIMATE;
while ((ANIMATE = askUserForANIMATE()) != UserAction::QUIT) {
// We don't have to handle QUIT, since we do in the while check
switch (ANIMATE) {
case UserAction::TICK:
grid = gridTick(grid);
gridPrint(grid);
break;
case UserAction::ANIMATE:
for (int i = 0; i < ANIMATION_N_GENERATIONS; i++) {
grid = gridTick(grid);
clearConsole();
gridPrint(grid);
pause(ANIMATION_SLEEP_MS);
}
break;
}
}
std::cout << "Have a nice Life!" << std::endl;
return 0; return 0;
} }