Having Trouble with a 2-Dimensional Array

I am trying to make a program that will simulate Conway's Game of Life. The array that displays the game, a[i][j], is 22 cells high and 80 cells wide. I have a second array, b[i][j], that is used to store the number of neighbors of each cell before birthing/killing any of the cells. Here is the 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
#include <iostream>
using namespace std;
int main()
{
	char a[22][80];
	int b[22][80];
	int i;
	int j;
	char answer;
	int neighbors;
	for (i = 0; i < 22; i++)
	{
		for (j = 0; j < 80; j++)
		{
			a[i][j] = '0';
			cout << a[i][j];
			if (j == 79)
				cout << "\n";
		}
	}
	do
	{
		cout << "Enter a vertical coordinate (0-21).\n";
		cin >> i;
		cout << "Enter a horizontal coordinate (0-79).\n";
		cin >> j;
		a[i][j] = '*';
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		cout << "Would you like to make another live cell (y/n)?\n";
		cin >> answer;
	} while (answer == 'y');
	cout << "The Game of Life will begin. Type in 'c' to continue to the next generation. To end the simulation, type in 'e'.\n";
	do
	{
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				neighbors == 0;
				if (a[i-1][j-1] == '*')
					neighbors++;
				if (a[i-1][j] == '*')
					neighbors++;
				if (a[i-1][j+1] == '*')
					neighbors++;
				if (a[i][j-1] == '*')
					neighbors++;
				if (a[i][j+1] == '*')
					neighbors++;
				if (a[i+1][j-1] == '*')
					neighbors++;
				if (a[i+1][j] == '*')
					neighbors++;
				if (a[i+1][j+1] == '*')
					neighbors++;
				b[i][j] == neighbors;
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				if ((b[i][j] == 3)||((b[i][j] == 2)&&(a[i][j] == '*')))
				{
					a[i][j] == '*';
				}
				else
				{
					a[i][j] == '0';
				}
			}
		}
		cin >> answer;
	} while (answer != 'e');			
	return 0;
}


I believe my problem occurs somewhere in the do-while loop starting at line 41. It seems that no matter what happens, a[i][j] always displays the original array. No changes are ever made to it once the user has chosen the live cells to start with. What is wrong in my code?
You could try using the debugger to keep an eye on the values of your variables. Should be easy if you are using an IDE.

One observation:

if (a[i-1][j-1] == '*')

Will be accessing memory outside the array when i and j are zero.

I would also recommend not using i & j as array subscripts - use words like Row & Col, it will save you one day. I have seen it happen before where someone had 200 lines of code full of i's & j's with an error. Doing a find replace to change them to Row & Col made it much easier to spot the error - better yet, one is unlikely to make an error like MyArray[Row][Row]

Also , here is a better way to exit out of a loop (code I did to help someone else):

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
#include <iostream>
#include <locale>  //need this for std::toupper

//using namespace std   <--- don't do this

using std::cin;   //either do this, or put std:: before each std thing
using std::cout;  //I do this for things used a lot
using std::endl;  //then use std:: for things that are not

//Put function declarations here


int main() {

     bool Quit = false;
     char answer = 'N';

    while(!Quit) {

           //your code here

           cout << "Do you want to Quit? Y or N" << endl;
           cin >> answer;
          
           answer = std::toupper(answer);  //avoids testing answer twice
           if(answer == 'Y')  //test variable once
                 Quit = true;
    }

return 0;  //if all is well, something else if not
} //end of main

//Put function definitions here

Thank you for the advice. I will definitely use different subscripts for the future. I rewrote my code so that it would never access memory outside of the array. However, I still cannot get the array to change once the user has made the input to it. Here is the updated code:

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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <iostream>
using namespace std;
int main()
{
	char a[22][80];
	int b[22][80];
	int i;
	int j;
	char answer;
	int neighbors;
	for (i = 0; i < 22; i++)
	{
		for (j = 0; j < 80; j++)
		{
			a[i][j] = '0';
			cout << a[i][j];
			if (j == 79)
				cout << "\n";
		}
	}
	do
	{
		cout << "Enter a vertical coordinate (0-21).\n";
		cin >> i;
		cout << "Enter a horizontal coordinate (0-79).\n";
		cin >> j;
		a[i][j] = '*';
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		cout << "Would you like to make another live cell (y/n)?\n";
		cin >> answer;
	} while (answer == 'y');
	cout << "The Game of Life will begin. Type in 'c' to continue to the next generation. To end the simulation, type in 'e'.\n";
	do
	{
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				neighbors == 0;
				if (i == 0)
				{
					if (a[i+1][j] == '*')
						neighbors++;
					if (j == 0)
					{
						if (a[i][j+1] == '*')
							neighbors++;
						if (a[i+1][j+1] == '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i][j-1] == '*')
							neighbors++;
						if (a[i+1][j-1] == '*')
							neighbors++;
					}
					if ((j > 0)&&(j < 79))
					{
						if (a[i][j-1] == '*')
							neighbors++;
						if (a[i][j+1] == '*')
							neighbors++;
						if (a[i+1][j-1] == '*')
							neighbors++;
						if (a[i+1][j+1] == '*')
							neighbors++;
					}
				}
				if (i == 21)
				{
					if (a[i-1][j] == '*')
						neighbors++;
					if (j == 0)
					{
						if (a[i-1][j+1] == '*')
							neighbors++;
						if (a[i][j+1] == '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i-1][j-1] == '*')
							neighbors++;
						if (a[i][j-1] == '*')
							neighbors++;
					}
					if ((j > 0)&&(j < 79))
					{
						if (a[i-1][j-1] == '*')
							neighbors++;
						if (a[i-1][j+1] == '*')
							neighbors++;
						if (a[i][j-1] == '*')
							neighbors++;
						if (a[i][j+1] == '*')
							neighbors++;
					}
				}
				if ((i > 0)&&(i < 21))
				{
					if (j == 0)
					{
						if (a[i-1][j] == '*')
							neighbors++;
						if (a[i-1][j+1] == '*')
							neighbors++;
						if (a[i][j+1] == '*')
							neighbors++;
						if (a[i+1][j] == '*')
							neighbors++;
						if (a[i+1][j+1] == '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i-1][j-1] == '*')
							neighbors++;
						if (a[i-1][j] == '*')
							neighbors++;
						if (a[i][j-1] == '*')
							neighbors++;
						if (a[i+1][j-1] == '*')
							neighbors++;
						if (a[i+1][j] == '*')
							neighbors++;
					}
					if ((j > 0)&&(i < 79))
					{
						if (a[i-1][j-1] == '*')
							neighbors++;
						if (a[i-1][j] == '*')
							neighbors++;
						if (a[i-1][j+1] == '*')
							neighbors++;
						if (a[i][j-1] == '*')
							neighbors++;
						if (a[i][j+1] == '*')
							neighbors++;
						if (a[i+1][j-1] == '*')
							neighbors++;
						if (a[i+1][j] == '*')
							neighbors++;
						if (a[i+1][j+1] == '*')
							neighbors++;
					}	
				}
				b[i][j] == neighbors;
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				if ((b[i][j] == 3)||((b[i][j] == 2)&&(a[i][j] == '*')))
				{
					a[i][j] == '*';
				}
				else
				{
					a[i][j] == '0';
				}
			}
		}
		cin >> answer;
	} while (answer != 'e');	
	return 0;
}


I unfortunately do not have access to a debugger as far as I know of.
Is the use of the do loop (lines 41 to 183) the right choice of loop? The loop is always executed once, but you ask the questions at the start, but only get the input at the end on line 182, so the exit option doesn't work as expected.

There is also a bit of a rule that functions should not exceed 80 lines for readability. So you should split things into functions. The use of functions will make your code much easier to understand & debug.

Did you take on board what I was saying about i's & j's versus Row & Col?

Also the use of the bool variable to quit out of a loop? I personally avoid do loops where ever possible. One of the reasons they can create errors is because the end condition is so far away from the beginning of the loop.

And the use of sd::toupper?

I unfortunately do not have access to a debugger as far as I know of.


Are you using an IDE? If so which one? On my IDE (KDevelop on Linux) there is a debug menu, which uses gdb within the IDE.

If you are compiling from the cmd line, then you can debug by printing out values of variables with cout. This is tedious - much easier to learn the debugger.

If using gcc then the debugger is called gdb - which you can run from the cmd line. There is a tutorial in the documentation section of this site.

HTH
I have replaced the do loop on line 41 to a while loop similar to the one you showed me with the use of a bool variable, but that has not fixed my problem. I do understand the point you made about i's and j's vs Row and Col, and I will do that once I have time. I also know that my code looks very lengthy and should probably be seperated into different functions. I am not sure if I am using an IDE; I am making my codes on Linux with something called Terminal. I have tried debugging with cout, but I cannot pinpoint the exact location of my problem. The best I can get is that it is most likely anywhere after line 41.
Edit: See my comments below about how to do this better.
I am not sure if I am using an IDE; I am making my codes on Linux with something called Terminal.


OK, so you compile by entering a g++ command in the shell (the terminal). So you can use gdb from the command line. Check out this article on how to use it. You should also read the gdb man page. A man page is a "Manual Page". type man gdb from the shell command line.

http://www.cplusplus.com/articles/iwTbqMoL/


An IDE is a "Integrated Development Environment" - it consists of at least an editor, a compiler, and debugger, plus other tools.

It is very good that you are using the command line, because you will learn a lot doing that. However, I think some things are really tough - you can get more done with an IDE. The debugger being much easier to use in the IDE is a good example. The danger is not to get addicted to the IDE without understanding how it all works. So take a look to see how the IDE does compilation & linking & make, then go back a find out what all the options mean. Also check out the gcc man page.

I would highly recommend using more functions - it will help you see the logic in your code as well as other benefits. The other rule is that code should be easy for others to understand - in the real world, code is written by someone, then maintained or looked at by lots of other people. Using functions is one way to help achieve this.

I think there is an easier way to count neighbours of cells. Limit the processing , so that it never goes out of bounds of the main array - keep 1 row & col in from the edge. That is from [1][1] to [ArrayHeight-1][ArrayWidth-1]. The start position is always [Row-1][Col-1] from the main array, and you use a nested for loop that checks a 3 by 3 chunk of the main array - excluding the position that corresponds to the cell which is having it's neighbours counted.

I think that could all be done in about 10 lines of code.

You could also have an array of structs instead of 2 arrays. The struct stores the attributes of each cell like number of neighbours. This is good because it is easily scalable to add extra info.

See how you go with all that - good luck :)
Last edited on
gdb is not helping me much, probably because I have no clue as to how to use it. I know there is an easier way to count the neighbors, but I am more focused on having a functioning code right now than make my code look nice and short. i have no clue how to set up an array of structs for this code, though i agree that one would be extremely useful.
Last edited on
gdb is not helping me much, probably because I have no clue as to how to use it.


http://www.cplusplus.com/articles/iwTbqMoL/

That'll get you going. You should also check break and watch commands.
Last edited on
gdb is not helping me much, probably because I have no clue as to how to use it.


Did you read the article?

It might seem a drastic step, but can you install KDevelop or QtCreator IDE's say (there are lots of others available, but these 2 are good IMO)- then you can get more things done easier.

I know there is an easier way to count the neighbours, but I am more focused on having a functioning code right now than make my code look nice and short.


I am saying it would be easier to write 10 lines of code rather than debug the 185 lines you have already. It is not as hard as you might think.

You have a lot code that revolves around checking whether j == 79, my point is to restrict the outer for loops so they don't go out of bounds on the array. Also you have lots of if statements to check adjacency, that is easily done with more for loops.

Can you work it out from the info I have given?
I have read the article, but I am fairly new to c++. I could not make much sense out of it.

I cannot install anything while in Linux because when I run Linux, I am connected to my school's Mosaic server. It will not let me make any changes since I am not an admin.

I am trying to work with the info you have given to me to best of my extent, but I have yet to find the solution to my problem.
One thing I just noticed - line 165, the only place where the b array is involved:

b[i][j] == neighbors;

You have the equality operator instead of assignment.

Having said that, I think you should still have a go at rewriting.

Moshop's article is very easy to follow and well written, I thought

Do you have your own computer? You can download free IDE's such as Code::Blocks, that will work on windows.
Thank you for the observation. By switching that equality with an assignment as well as those that determine how many neighbors there are, I now have a new outcome. Here is the code so far (I have not yet rewritten it, I know. I'm working on it):


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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
#include <iostream>
using namespace std;
int main()
{
	char a[22][80];
	int b[22][80];
	char newa[22][80];
	int i;
	int j;
	char answer;
	int neighbors;
	bool quit = false;
	for (i = 0; i < 22; i++)
	{
		for (j = 0; j < 80; j++)
		{
			a[i][j] = '0';
			cout << a[i][j];
			if (j == 79)
				cout << "\n";
		}
	}
	do
	{
		cout << "Enter a vertical coordinate (0-21).\n";
		cin >> i;
		cout << "Enter a horizontal coordinate (0-79).\n";
		cin >> j;
		a[i][j] = '*';
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		cout << "Would you like to make another live cell (y/n)?\n";
		cin >> answer;
	} while (answer == 'y');
	cout << "The Game of Life will begin. Type in 'c' to continue to the next generation. To end the simulation, type in 'e'.\n";
	while (!quit)
	{
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				cout << a[i][j];
				if (j == 79)
					cout << "\n";
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				neighbors == 0;
				if (i == 0)
				{
					if (a[i+1][j] = '*')
						neighbors++;
					if (j == 0)
					{
						if (a[i][j+1] = '*')
							neighbors++;
						if (a[i+1][j+1] = '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i][j-1] = '*')
							neighbors++;
						if (a[i+1][j-1] = '*')
							neighbors++;
					}
					if ((j > 0)&&(j < 79))
					{
						if (a[i][j-1] = '*')
							neighbors++;
						if (a[i][j+1] = '*')
							neighbors++;
						if (a[i+1][j-1] = '*')
							neighbors++;
						if (a[i+1][j+1] = '*')
							neighbors++;
					}
				}
				if (i == 21)
				{
					if (a[i-1][j] = '*')
						neighbors++;
					if (j == 0)
					{
						if (a[i-1][j+1] = '*')
							neighbors++;
						if (a[i][j+1] = '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i-1][j-1] = '*')
							neighbors++;
						if (a[i][j-1] = '*')
							neighbors++;
					}
					if ((j > 0)&&(j < 79))
					{
						if (a[i-1][j-1] = '*')
							neighbors++;
						if (a[i-1][j+1] = '*')
							neighbors++;
						if (a[i][j-1] = '*')
							neighbors++;
						if (a[i][j+1] = '*')
							neighbors++;
					}
				}
				if ((i > 0)&&(i < 21))
				{
					if (j == 0)
					{
						if (a[i-1][j] = '*')
							neighbors++;
						if (a[i-1][j+1] = '*')
							neighbors++;
						if (a[i][j+1] = '*')
							neighbors++;
						if (a[i+1][j] = '*')
							neighbors++;
						if (a[i+1][j+1] = '*')
							neighbors++;
					}
					if (j == 79)
					{
						if (a[i-1][j-1] = '*')
							neighbors++;
						if (a[i-1][j] = '*')
							neighbors++;
						if (a[i][j-1] = '*')
							neighbors++;
						if (a[i+1][j-1] = '*')
							neighbors++;
						if (a[i+1][j] = '*')
							neighbors++;
					}
					if ((j > 0)&&(i < 79))
					{
						if (a[i-1][j-1] = '*')
							neighbors++;
						if (a[i-1][j] = '*')
							neighbors++;
						if (a[i-1][j+1] = '*')
							neighbors++;
						if (a[i][j-1] = '*')
							neighbors++;
						if (a[i][j+1] = '*')
							neighbors++;
						if (a[i+1][j-1] = '*')
							neighbors++;
						if (a[i+1][j] = '*')
							neighbors++;
						if (a[i+1][j+1] = '*')
							neighbors++;
					}	
				}
				b[i][j] = neighbors;
			}
		}
		for (i = 0; i < 22; i++)
		{
			for (j = 0; j < 80; j++)
			{
				if ((b[i][j] == 3)||((b[i][j] == 2)&&(a[i][j] = '*')))
				{
					a[i][j] = '*';
				}
				else
				{
					a[i][j] = '0';
				}
			}
		}
		cin >> answer;
		if (answer == 'e')
			quit = true;
	}	
	return 0;
}


For the output, the first time the user presses 'c' (or really any character other than 'e'), every cell in the array dies, 0, except for a[0][0], no matter what pattern the user inputs. For the next generation and each one after, a[0][0] dies and the board stays that way as expected.

I noticed that on line 174, if I replace the equality operators with an assignment, the whole array a[i][j] will become alive and stay that way. I am guessing that the problem I am currently experiencing has something to do with assignments and equality operators.
I noticed that on line 174, if I replace the equality operators with an assignment, the whole array a[i][j] will become alive and stay that way


But you should not have assignments in an if condition. If you do, and it changes your outcome - does that imply you should have those assignments before the if statement?

In your existing code, lines 184 to 186 should be just after the while on line 44. Use break to exit the while early if answer is 'e'.

The code to decide whether to continue / quit should be before the while loop - so this gets rid of the while loop that was solely for that purpose.

Any way, I still think it would be easier to write the neighbour checking algorithm I described earlier.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const unsigned int MAXROWS = 22;
const unsigned int MAXCOLS = 80;

unsigned int Row;  // rows in the main array
unsigned int Col;  // cols in the main array

unsigned int NRow; // Rows in the neighbour checking
unsigned int NCol; // Cols in the neighbour checking

char Board[MAXROWS][MAXCOLS];  // The game board
char Adj[MAXROWS][MAXCOLS];  // stores the neighbour count

for (Row = 1;Row < MAXROWS-1;Row++) {  //ensure we don't go out of bounds
      for(Col = 1;Col < MAXCOLS-1; Col++) {
           for (NRow = Row -1;NRow < (Row + 1);NRow++) {
                for (NCol = Col -1 ;NCol < (Col +1);NCol++) {
                       if (Board[NRow][NCol] == '*')  {
                             (Adj[NRow][NCol])++

                       }
                }
           }
      }
}


This code isn't tested - I just typed it straight in. It doesn't assign an adjacency count for the boundary cells - easy to change by putting a switch that calls functions to deal with each of the 4 lines of boundary cells.

Can you see how this is easier than having lots of if statements?

I have used another array to store the adjacency count. As I said, the use of a struct would be better. Just make a struct which stores the relevant info. Then make an array of these structs. Look in the references pages (Top left of this page) to see how to do this.

Edit:

With cout, you can build up the output in stages - no need to have it all on 1 line of code. So this will have the same effect as what you have:

1
2
3
cout << "The Game of Life will begin. " ;
cout << "Type in 'c' to continue to the next generation. "; 
cout << "To end the simulation, type in 'e'.\n";


This helps in not having more than 80 chars per line of code for readability.

HTH
Last edited on
Topic archived. No new replies allowed.