Conway's Game of Life

Design a simple command line based version of Conway's Game of Life. See rules here: https://www.wikiwand.com/en/Conway%27s_Game_of_Life

#include <iostream>
#include <vector>
#include <math.h>
#include <chrono>
#include <thread>
#include <conio.h>

using namespace std;

class State {
public:
    State(int y, int x, bool state) {
        x_pos = x;
        y_pos = y;
        alive = state;
    }

    int x_pos;
    int y_pos;
    bool alive;
private:

};

inline void print2d(vector<vector<char>> &nums)
{
    for (int i = 0; i < nums.size(); i++) {
        for (int j = 0; j < nums.at(i).size(); j++) {
            cout << nums.at(i).at(j) << ' ';
        }
        cout << endl;
    }
}

vector<vector<char>> createField(int length, int height) {
    vector<vector<char>> field;
    vector<char> temp;

    for (int i = 0; i < length; i++) {
        for (int j = 0; j < height; j++) {
            temp.push_back(' ');
        }
        field.push_back(temp);
        temp.clear();
    }
    return field;
}

inline int countNumberOfLiveCells(vector<vector<char>> &currentState, int y, int x) {
    // 8 possible neighbors
    int count = 0;
    if (currentState.at(y + 1).at(x) == 'O') count++;
    if (currentState.at(y + 1).at(x + 1) == 'O') count++;
    if (currentState.at(y).at(x + 1) == 'O') count++;
    if (currentState.at(y - 1).at(x + 1) == 'O') count++;
    if (currentState.at(y - 1).at(x) == 'O') count++;
    if (currentState.at(y - 1).at(x - 1) == 'O') count++;
    if (currentState.at(y).at(x - 1) == 'O') count++;
    if (currentState.at(y + 1).at(x - 1) == 'O') count++;
    return count;
}

void getNextState(vector<vector<char>> *currentState) {
    vector<State> updateCells;

    for (int i = 1; i < (*currentState).size() - 1; i++) {
        for (int j = 1; j < (*currentState).at(i).size() - 1; j++) {
            int liveCells = countNumberOfLiveCells((*currentState), i, j);
            // Analyze 4 cases per cell - deaths and births occur simultaneously
            // 1) Any live cell with fewer than two live neighbours dies, as if caused by under-population.
            // 3) Any live cell with more than three live neighbours dies, as if by over-population.
            // 2) Any live cell with two or three live neighbours lives on to the next generation. (inverse of statement 1)
            if ((*currentState).at(i).at(j) == 'O') {
                if (liveCells < 2 || liveCells > 3) {
                    updateCells.emplace_back(i, j, false);
                }
                    //(*currentState).at(i).at(j) = ' ';
            }
            // 4) Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
            else {
                if (liveCells == 3)
                    updateCells.emplace_back(i, j, true);
                    //(*currentState).at(i).at(j) = 'O';
            }
        }
    }

    // update new generation
    for (int i = 0; i < updateCells.size(); i++) {
        int y_pos = (updateCells.at(i)).y_pos;
        int x_pos = (updateCells.at(i)).x_pos;
        if (updateCells.at(i).alive == true) {
            (*currentState).at(y_pos).at(x_pos) = 'O';
        }
        else (*currentState).at(y_pos).at(x_pos) = ' ';
    }

}


inline void runGame(vector<vector<char>> &field, int sleep) {
    while (true) {
        // 1) Display / print the updated field
        print2d(field);

        // 2) Wait a set amount of time
        std::this_thread::sleep_for(std::chrono::milliseconds(sleep));

        // 3) Get the updated field by modifying the current field in mem
        getNextState(&field);

        // 4) Clear the window
        system("cls");
    }
}

void loopInitializer(vector<vector<char>> &enviroment, vector < pair<int, int>> thingToCreate, pair<int, int> &origin) {
    for (int i = 0; i < thingToCreate.size(); i++) {
        enviroment.at(origin.first + thingToCreate.at(i).first).at(origin.second + thingToCreate.at(i).second) = 'O';
    }
}

void initialize(int length, int height, int choice, int sleep) {
    vector<vector<char>> enviroment = createField(length, height);
    pair<int, int> origin(length / 2, height / 2);

    switch (choice) {
    /*
    think cartisian cord system, with the y (origin.first) sign flipped
    y,x not x,y
    */
    case 1:
    {
        vector < pair<int, int>> glider =
        { { 0,0 },{ 1,1 },{ 1,2 },{ 0,2 },{ -1,2 } };
        loopInitializer(enviroment, glider, origin);
        break;
    }

    case 2:
    {
        vector < pair<int, int>> blinker =
        { { -1,0 },{ 0,0 },{ 1,0 } };
        loopInitializer(enviroment, blinker, origin);
        break;
    }

    case 3:
    {
        vector < pair<int, int>> toad =
        { { 0,1 },{ -1,1 },{ -1,0 },{ 0,0 },{ 0,2 },{ -1,-1 } };
        loopInitializer(enviroment, toad, origin);
        break;
    }

    case 4:
    {
        vector < pair<int, int>> beacon =
        { {0,0},{ 0,1 },{ 1,0 },{ 2,3 },{ 3,3 },{ 3,2 } };
        loopInitializer(enviroment, beacon, origin);
        break;
    }
    case 5:
    {
        vector < pair<int, int>> pulsar =
        { {-1,2},{ -1,3 },{ -1,4 },{ -2,1 },{ -3,1 },{ -4,1 },
        { -6,2 },{ -6,3 },{ -6,4 },{ -2,6 },{ -3,6 },{ -4,6 },

        { -1, -2 },{ -1,-3 },{ -1,-4 },{ -2,-1 },{ -3,-1 },{ -4,-1 },
        { -6,-2 },{ -6,-3 },{ -6,-4 },{ -2,-6 },{ -3,-6 },{ -4,-6 },

        { 1,-2 },{ 1,-3 },{ 1,-4 },{ 2,-1 },{ 3,-1 },{ 4,-1 },
        { 6,-2 },{ 6,-3 },{ 6,-4 },{ 2,-6 },{ 3,-6 },{ 4,-6 },

        { 1,2 },{ 1,3 },{ 1,4 },{ 2,1 },{ 3,1 },{ 4,1 },
        { 6,2 },{ 6,3 },{ 6,4 },{ 2,6 },{ 3,6 },{ 4,6 } };

        loopInitializer(enviroment, pulsar, origin);
        break;
    }
    case 6:
    {
        vector < pair<int, int>> beacon =
        { { 0,0 },{ 0,1 },{ 0,2 },{ -1,0 },{ -1,1 },{ -1,2 },
        { 1,0 },{ 1,1 },{ 1,2 },{ 2,0 },{ 2,1 },{ 2,2 },
        { -2,0 },{ -2,2 },{ -3,0 },{ -3,1 },{ -3,2 },
        { 3,0 },{ 3,2 },{ 4,0 },{ 4,1 },{ 4,2 },
        };
        loopInitializer(enviroment, beacon, origin);
        break;
    }

    case 7:
    {
        vector < pair<int, int>> spaceShip =
        { { 0,0 },{ 0,3 },{ 2,0 },{ 1,4 },{ 2,4 },{ 3,1 },
        { 3,2 },{ 3,3 },{ 3,4 }
        };
        loopInitializer(enviroment, spaceShip, origin);
        break;
    }

    default:
        cout << "Invalid Choice" << endl;
    }
    runGame(enviroment, sleep);
}


// https://www.wikiwand.com/en/Conway's_Game_of_Life
int main()
{
    cout << "        Welcome to Conways Game of Life" << endl;
    cout << " For best result, expand your console window size" << endl;
    cout << "Please select one of the following classic fields" << endl;

    int choice;

    cout << "\n     Conway Menu\n"
        << "1. Glider\n"
        << "2. Blinker\n"
        << "3. Toad\n"
        << "4. Beacon\n"
        << "5. Pulsar\n"
        << "6. Pentadecathlon \n"
        << "7. Lightweight spaceship \n"
        << "       Your choice >> ";

    cin >> choice;

    int gottaGoFast;
    cout << "How fast do you want each frame? (in milliseconds) - recommended 125" << endl;
    cin >> gottaGoFast;

    switch (choice)
    {
        case 1:
            initialize(35, 60, 1, gottaGoFast);
            break;

        case 2:
            initialize(35, 60, 2, gottaGoFast);
            break;

        case 3:
            initialize(35, 60, 3, gottaGoFast);
            break;

        case 4:
            initialize(35, 60, 4, gottaGoFast);
            break;

        case 5:
            initialize(35, 60, 5, gottaGoFast);
            break;

        case 6:
            initialize(35, 60, 6, gottaGoFast);
            break;

        case 7:
            initialize(35, 60, 7, gottaGoFast);
            break;

        default:
            cout << "Invalid Choice" << endl;
    }

}

Last updated