Game of Life

Hi everyone,

I am struggling to write a program for Conway's Game of life. I am a relative beginner so my mistake may seem obvious. I am getting a problem with my 'display' function, but I think that the root of my problem is that I am not sure how to handle cells on the boundaries. These cells do not have 8 neighbors so they are unlike the others but I don't know how to deal with them. I am using xcode on a macbook (not sure if that matters). Any help would be greatly appreciated!


Here is my code so far:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126

#include <iostream>
#include <vector>
#include <unistd.h>
#include <cstdlib>
using namespace std;

// Global variables

const int ROWS=21;
const int COLS=80;
const char ALIVE= 'x';
const char DEAD= ' ';

// Function prototypes 
void generation(vector< vector<char> > &world, vector< vector<char> > &worldcopy);

void display(vector< vector<char> > &world);

int main()
{
    vector< vector<char> > world(ROWS, vector<char>(COLS, DEAD));
    vector< vector<char> > worldcopy(ROWS, vector<char>(COLS, DEAD));
    
    // Set the initial alive cell configuration
    
    world[2][2] = world [2][3] = world[2][4] = ALIVE;
    
    while(true)
    {
        system("clear");
        display(world);
        usleep(80000);
        generation(world, worldcopy);
        
        return 0;
    }
    

}


void generation(vector< vector<char> > &world, vector< vector<char> > &worldcopy)
{
    int count=0;
    
    for (int i=0; i<ROWS; i++)
    {
        for (int j=0; j<COLS; j++)
        {
            // Check neighbor cells for life
            
            if (worldcopy[i-1][j+1]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i][j+1]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i+1][j+1]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i-1][j]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i+1][j]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i-1][j-1]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i][j-1]==ALIVE)
            {
                count++;
            }
            if (worldcopy[i+1][j-1]==ALIVE)
            {
                count++;
            }
            
            // Rules
            // Lonliness
            if ((worldcopy[i][j]==ALIVE) && (count==0 || count ==1))
            {
                world[i][j] = worldcopy[i][j];
                world[i][j] = DEAD;
            }
            // Lives to next gen.
            else if ((worldcopy[i][j]==ALIVE) && (count==2 || count==3))
            {
                world[i][j] = worldcopy[i][j];
                world[i][j] = ALIVE;
            }
            // Death by overcrowding
            else if (worldcopy[i][j]==ALIVE && count >3)
            {
                world[i][j] = worldcopy[i][j];
                world[i][j] = DEAD;
            }
            // Birth
            else if(worldcopy[i][j]==ALIVE && count==3)
            {
                world[i][j] = worldcopy[i][j];
                world[i][j] = ALIVE;
            }
        }
    }
}

void display(vector< vector<char> > &world)
{
    
    for (int i=0; i <=ROWS; i++)
    {
        for (int j=0; j<=COLS; j++)
        {
            cout << world[i][j];
        }
        cout << endl;
    }
}
I notice that the for loops in the display function are iterating beyond the size of the vector. If the size of the vector is ROWS then iterate to i<ROWS, just as it is in generate.
Have you considered making the 'gameboard' for Life toroidally-linked? Maybe just applying that concept (since you aren't using linked lists or nodes and such) would help you.

http://en.wikipedia.org/wiki/Doughnut_theory_of_the_universe
http://en.wikipedia.org/wiki/Toroidal_graph

I had an assignment back when I first took C++ at my University where we had to make Conway's Game of Life, but we had to use doubly-linked lists for each row. He then raised the bar after the first due date and we had to make it 'toroidally-linked', so the top row of the grid was to be doubly-linked with the bottom row, and same for each side of the grid. It made it much more effective as an exercise with pointers, and our Game of Life was able to fully function on the edges due to the future generations being dependent on a full eight neighbors.

The toroidal approach is possible with your implementation as well. Your cells on the outside edges will have to be able to check the edge-cells on the opposite side (thinking toroidally, there is no edge, each side wraps around and is connected to the other). But for you it may just have to be a clever use of index math. Look into it and see if that might help you.

Good luck! :)
nkendra: Thanks, I didn't realize that issue the first run through!

ENIGMAx: That is actually a really interesting idea. I think at this point, that may be a little beyond my programming capabilities, and what is expected of me by my professor. Any chance you care to expand on how to go about that just to satisfy my intrest?
You would want to create special cases that target only the boundary cells. Then depending on each gameboard boundary in question you could perform the correct checks to see if neighbors on an opposite boundary are alive or dead to determine the next generation.

For a start, you'd have to consider the different boundaries' locations in your grid. Off the top of my head, there'd at least be cases for the leftmost column of the grid (but not counting the top or bottom cells) the rightmost column of the grid (also excluding the top and bottom cells), and same for top and bottom rows of the grid (excluding leftmost and rightmost cells in those two cases). There would then be a case individually for each corner of your gameboard/grid. It makes for some thinking and overhead initially (being that this makes 8 specialty cases in addition to your ordinary generation process), but in the end is well worth both the learning experience and the results.

Since you're using a 2-D grid, this is going to be easier than a single indexed array/vector or 1-D implementation (which still isn't that difficult when you understand the concept).

**Note: If you don't want unnecessary information that is not directly related to the implementation of your program, skip this next bit of text. If you'd like to hear more about this concept in a broad (and most likely confusing) context, read on!:
A 1-D gameboard seems odd at first, but think of it this way:
Say the user specifies the gameboard dimension size, and this value is n. Keeping it simple with a square gameboard (instead of variable length and width sizes), the entire number of cells is then n * n = N, which is what you have in a 2-D array implementation of [n][n], where both number of columns is n, and number of rows is n. For the sake of fun, I'll add just a little more for a 1-D grid implementation. The 1-D grid is represented by a singly-indexed array/vector of max index size N. You can see that it is confusing at first, but manipulating n as the index can get you to different rows, columns, etc. Using N-1, you can start at the end of the array. Subtracting each subsequent multiple of n from N puts you at each 'row' up from the bottom, and the same works adding multiples of n from index 0 (or the beginning of the grid, (0,0) for you, upper left corner).
// end of confusing bit

Now to the point at which I said
But for you it may just have to be a clever use of index math
and for your case my statement was wrong. You don't need to use clever index math at all because you are using a 2-D grid. Hooray!

Assuming n is your max index size for a row or column:

You should be able to access the beginning of each row (thus the left side of the grid) by:
[row index][0].
The end of each row would then be:
[row index][n].

Hope this helps.
Topic archived. No new replies allowed.