diff --git a/src/life.cpp b/src/life.cpp index c937402..e6d5e09 100755 --- a/src/life.cpp +++ b/src/life.cpp @@ -12,6 +12,8 @@ const char *const FORMAT_UNDERSCORE = "\033[4m"; const char *const END_FORMATTING = "\033[0m"; +const int ANIMATION_SLEEP_MS = 100, ANIMATION_N_GENERATIONS = 20; + void printWelcome() { std::cout << "Welcome to the TDDD86 Game of Life,\n" << "a simulation of the lifecycle of a bacteria colony.\n" @@ -52,17 +54,19 @@ std::string fileRead(const char *filePath) { return content; } -enum UserAction { NO_ACTION, ACTION, TICK, QUIT }; +enum UserAction { ANIMATE, TICK, QUIT }; -UserAction askUserForAction() { - std::string userInput = input("a)nimate, t)ick, q)uit? "); - if (userInput == "a") - return UserAction::ACTION; - else if (userInput == "t") - return UserAction::TICK; - else if (userInput == "q") - return UserAction::QUIT; - return UserAction::NO_ACTION; +UserAction askUserForANIMATE() { + while (true) { + std::string userInput = input("a)nimate, t)ick, q)uit? "); + if (userInput == "a") + return UserAction::ANIMATE; + else if (userInput == "t") + return UserAction::TICK; + else if (userInput == "q") + return UserAction::QUIT; + std::cout << "The input '" << userInput << "' is an invalid option!\n"; + } } std::vector split(const std::string &s, const char at) { @@ -81,6 +85,75 @@ std::vector split(const std::string &s, const char at) { enum Cell { ALIVE, DEAD }; +void gridPrint(Grid &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 &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 gridTick(Grid &grid) { + Grid 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() { printWelcome(); @@ -103,10 +176,10 @@ int main() { std::cerr << "No column, row header, in the file!" << std::endl; return 1; } - int columns, rows; + int rows, columns; try { - columns = std::stoi(lines[0]); - rows = std::stoi(lines[1]); + rows = std::stoi(lines[0]); + columns = std::stoi(lines[1]); } catch (const std::invalid_argument &e) { std::cerr << "Column or row wasn't a number" << std::endl; return 1; @@ -122,7 +195,7 @@ int main() { Grid grid(columns, rows); for (int x = 0; x < rows; x++) { - std::string &row = lines[x]; + std::string &row = lines[x + 2]; if (columns < row.size()) { std::cerr << "There's less columns, than described in the header!" << 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; }