Search code examples
c++conways-game-of-life

Conway's Game of Life help me understand this unexpected output


I would like some help understanding why my program is printing a grid of

....................
....................
....................
....................
....................
...............OOOOO
OOOOOOOOOOOOO.......
....................
....................
....................

The correct output would be so:

....................
....................
....................
....................
....................
....................
....................
.............O.O....
..............OO....
..............O..... 

The way I wrote it is to create a copy of the old state and manipulate it using the rules of the game. After I check every cell, I store count of the number of neighbors alive for that cell. IF the count is greater than 3 or less than two, the cell will die.

If a cell has a count of 2 or 3 neighbors, it remains alive. If a dead cell has a count of 3, it becomes alive. These rules are directly applied to the copy version instead of the old and then print the copy.

I've tried using a debugger but I'm still unsure of how to use it properly. I haven't notice any red flags as of yet. Here's my code:

#include <iostream>
#include <vector>
using std::vector;
using std::cout;
vector<vector<bool> > world = {
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
};

void generate(const vector<vector<bool> >&g,vector<vector<bool> >&newworld)
{
    int count = 0;
    newworld = g;
    for(size_t i = 0; i < g.size();i++) {
        for(size_t j = 0; j < g[i].size();j++) {
            int x = g.size(); //I rows
            int y = g[i].size(); //J columns
            //wrap the edges with formula (x+n)%n  where n = NumOfRows or NumOfCol
            if(g[(((i+1)+x)%x)][(((j-1)+y)%y)]==true){//top left
            count++;
            }
             else if(g[(((i+1)+x)%x)][j]==true){//top middle
            count++;
            }
             else if(g[(((i+1)+x)%x)][(((j+1)+y)%y)]==true){//top right
            count++;
            }
             else if(g[i][(((j-1)+y)%y)]==true){//left cell
            count++;
            }
             else if(g[i][(((j+1)+y)%y)]==true){//right cell
            count++;
            }
             else if(g[(((i-1)+x)%x)][(((j-1)+y)%y)]==true){ //bottom left
            count++;
            }
             else if(g[(((i-1)+x)%x)][j]==true){//bottom middle
            count++;
            }
             else if(g[(((i-1)+x)%x)][(((j+1)+y)%y)]==true){//bottom right
            count++;
            }

        if (g[i][j]) {
            if(count > 3 || count < 2) {//if alive cell has more than 3 or less than 2, die
            newworld[i][j] = false;
            }
            else if (count == 2 || count == 3) { //remain the same 
                newworld[i][j] = g[i][j];
            }
        }
        else if (g[i][j] == false) {//dead come alive
        if(count == 3) {
        newworld[i][j] = true;
        }

            }
        }
    }
}

void display(vector<vector<bool> >&a)
{
    for(size_t row = 0; row <a.size(); row++) {
        for(size_t column = 0; column <a[row].size(); column++){
            if (a[row][column]) {
                cout << 'O';
            }
             else {
                cout << '.';
             }
        }
        cout << '\n';
    }          
}        

int main()
{
    vector<vector<bool> > newworld;
    generate(world,newworld);
    display(newworld);
    return 0;
}

Solution

  • The function generate has (at least) two problem.

    • count is initialized outside the nested loops, so it's never reset to zero (as it should, for every cell) and keeps growing.

    • All the conditions are mutually exclusive, so whenever one it's met, the others are skipped. There shouldn't be any else if, but only ifs.

    Keeping the data structure you chose, you can rewrite that function as

    using gof_t = std::vector<std::vector<bool>>;
    
    void generate(gof_t& g, gof_t& newworld)
    {
        for(size_t i = 0, x = g.size(); i < x; i++)
        {
            for(size_t j = 0, y = g[i].size(); j < y; j++)
            {
                size_t i_prev = (i + x - 1) % x;
                size_t i_next = (i + 1) % x;
                size_t j_prev = (j + y - 1) % y;
                size_t j_next = (j + 1) % y;
    
                int count = g[i_prev][j_prev] + g[i_prev][j] + g[i_prev][j_next]
                          + g[i     ][j_prev]                + g[i     ][j_next]
                          + g[i_next][j_prev] + g[i_next][j] + g[i_next][j_next];
    
                newworld[i][j] = g[i][j] ? (count == 2 || count == 3) : (count == 3);
            }
        }
        std::swap(g, newworld); // <-- Passing by non const reference, we can swap without copying
    }
    

    Live (pun intended), here.