// This is the CPP file you will edit and turn in. // Also remove these comments here and add your own. // TODO: remove this comment header #include "grid.h" #include "lifeutil.h" #include "unistd.h" #include #include #include const char *const FORMAT_UNDERSCORE = "\033[4m"; const char *const END_FORMATTING = "\033[0m"; void printWelcome() { std::cout << "Welcome to the TDDD86 Game of Life,\n" << "a simulation of the lifecycle of a bacteria colony.\n" << "Cells (X) live and die by the following rules:\n" << " - A cell with 1 or fewer neighbours dies.\n" << " - Locations with 2 neighbours remain stable.\n" << " - Locations with 3 neighbours will create life.\n" << " - A cell with 4 or more neighbours dies." << std::endl; } std::string input(const char *message) { if (message != nullptr) { std::cout << message << FORMAT_UNDERSCORE; std::flush(std::cout); } std::string ret; std::getline(std::cin, ret); if (message != nullptr) std::cout << END_FORMATTING; return ret; } bool fileExists(const char *fileName) { return access(fileName, 0) == 0; } std::string fileRead(const char *filePath) { std::ifstream file(filePath, std::ios::binary); if (!file.is_open()) throw std::runtime_error("File couldn't be found"); std::string content; const int BUF_LENGTH = 1024; char buf[BUF_LENGTH]; while (file.read(buf, BUF_LENGTH)) content.append(buf, file.gcount()); content.append(buf, file.gcount()); file.close(); return content; } enum UserAction { NO_ACTION, ACTION, 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; } std::vector split(const std::string &s, const char at) { std::vector ret; size_t last = 0, idx; while ((idx = s.find(at, last)) != std::string::npos) { std::string sub = s.substr(last, idx - last); ret.push_back(sub); last = idx + 1; } ret.push_back(s.substr(last)); return ret; } enum Cell { ALIVE, DEAD }; int main() { printWelcome(); std::string inputFile; while (true) { inputFile = input("Grid input file name? "); if (fileExists(inputFile.c_str())) break; std::cout << "File '" << inputFile << "' doesn't exist!" << std::endl; } std::string fileContents; try { fileContents = fileRead(inputFile.c_str()); } catch (std::exception &e) { std::cerr << "Failed to read file: " << e.what() << std::endl; return 1; } std::vector lines = split(fileContents, '\n'); if (lines.size() <= 2) { std::cerr << "No column, row header, in the file!" << std::endl; return 1; } int columns, rows; try { columns = std::stoi(lines[0]); rows = std::stoi(lines[1]); } catch (const std::invalid_argument &e) { std::cerr << "Column or row wasn't a number" << std::endl; return 1; } catch (const std::out_of_range &e) { std::cerr << "Column or row was too large" << std::endl; return 1; } if (lines.size() - 2 < rows) { std::cerr << "There's less rows than described in the header!" << std::endl; return 1; } Grid grid(columns, rows); for (int x = 0; x < rows; x++) { std::string &row = lines[x]; if (columns < row.size()) { std::cerr << "There's less columns, than described in the header!" << std::endl; return 1; } for (int y = 0; y < columns; y++) { char sign = row[y]; switch (sign) { case '-': grid.set(x, y, Cell::DEAD); break; case 'X': grid.set(x, y, Cell::ALIVE); break; default: std::cerr << "Invalid option '" << sign << "' in grid description!" << std::endl; return 1; } } } return 0; }