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:
#include <iostream>
usingnamespace 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):
#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:
#include <iostream>
usingnamespace 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.
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.
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.
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.
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.
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.
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):
#include <iostream>
usingnamespace 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.
constunsignedint MAXROWS = 22;
constunsignedint MAXCOLS = 80;
unsignedint Row; // rows in the main array
unsignedint Col; // cols in the main array
unsignedint NRow; // Rows in the neighbour checking
unsignedint 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.