152 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			152 lines
		
	
	
		
			4.3 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| // 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 <fstream>
 | |
| #include <iostream>
 | |
| #include <string>
 | |
| 
 | |
| 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<std::string> split(const std::string &s, const char at) {
 | |
|     std::vector<std::string> 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<std::string> 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<Cell> 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;
 | |
| }
 |