Before anyone says it, yes, I know this is HORRIBLY written. I’m not a professional. What I am is frustrated, since for some reason in the main function, after telling the program you don’t want to use the preset you’ve selected, it messes up. Try it. Follow the instructions, go to presets, choose any preset and say no. The line “cancelling…” will display after the program waits and at the start of the next frame, despite there being a system("clear");
in between (inside of DrawPlaySpace();
). I have no clue what’s happening here.
NOTE: there are a few unfinished features here, like the stable loop detection and the randomized preset. If I can’t figure those out myself either, expect another question here lol
ANOTHER NOTE: please don’t steal this, I neglected my classwork so much for this and I’m very proud of my silly little stupid Conway’s Game of Life son, as frustrated as I may be
EDIT: I added comments (yay)
#include <iostream>
#include <string>
#include <unistd.h>
#include <stdlib.h>
#include <chrono>
#include <thread>
using namespace std::this_thread; // sleep_for, sleep_until
using namespace std::chrono; // nanoseconds, system_clock, seconds
using namespace std;
bool lastInputBad = false; //true if you input something invalid at the setup menu, so the text for it can be at the bottom of the screen
bool lastFrame = false; //allows the system to actually display the final frame if it is ended by either a total wipe or [WIP] an infinite loop
int iterations = 0; //records how many times the sim has updated
int similaritiesToCurrent[25][25]; //stores how many of the last 10 frames have the living tiles in each spot
bool stableLock = false; //decides whether the sim is locked in a stable loop forever
bool stableDetection[10][25][25]; //stores the last 10 25x25 frames
bool playSpace[25][25]; //the current frame
bool nextGen[25][25]; //the place we store the next frame while we're computing it so it doesn't mess with the computations
bool setup = true; //used to decide if certain text should be displayed when drawing the current frame
string userInput; //records the user's input in the main menu
string presetSelection; //records which preset the user has selected at the main menu
string confirmation; //records the Y or N of the user when asked if they want the current preset
int cursor[2] {0, 0}; //position of the cursor during setup
int totalLife = 0; //total number of living cells on the board
void RecordGen() { //moves each of the last 10 frames back one spot and then records the current frame in the new lowest spot
for (int h = 9; h >= 0; h--) {
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
if (h == 0) {
stableDetection[0][i][j] = playSpace[i][j];
} else if (h < 9) {
stableDetection[h + 1][i][j] = stableDetection[h][i][j];
}
}
}
}
}
void DetectStability() { //[BROKEN] uses the record of the last 10 frames to decide how many of them are identical (to decide if the sim is infinitely looping)
stableLock = true;
for (int i = 0; i < 25; i++) {
for (int j = 0; i < 25; i++) {
similaritiesToCurrent[i][j] = 0;
}
}
for (int h = 1; h < 10; h++) {
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
if (stableDetection[h][i][j] && playSpace[i][j]) {
similaritiesToCurrent[i][j]++;
}
}
}
}
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
if (similaritiesToCurrent[i][j] < 6 && (stableDetection[0][i][j] || stableDetection[1][i][j])) stableLock = false;
//cout<<"Cell at "<<i<<", "<<j<<" has "<<similaritiesToCurrent[i][j]<<" similarities to the last 10 frames\n";
//for debugging, uncomment to use^
}
}
}
void ResetPlaySpace() { //resets the board to all dead cells
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
playSpace[i][j] = 0;
nextGen[i][j] = 0;
}
}
}
void DrawPlaySpace(bool isSetup) { //draws the board to the terminal. Lots of use of the isSetup boolean since it decides how some things look (and then I can use the same rendering function no matter what)
system("clear");
if (isSetup) cout<<"This is the setup.\nBelow you can see the board.\nType 'w', 'a', 's', or 'd' and press enter to move the cursor around.\nTo toggle the selected cell, type ' ' and hit enter.\nOnce you are happy with the board, type 'start' and hit enter, and watch the cells do their thing!\nAlternatively, type 'presets' and hit enter to view the list of pre-made boards";
if (!isSetup) {
cout<<"\n\n __________________________________________________\n";
} else {
cout<<"\n ";
for (int i = 0; i < 25; i++) {
if (i < 9) cout<<"0";
cout<<i + 1<<" ";
}
cout<<"\n";
}
for (int i = 0; i < 25; i++) {
if (!isSetup) {
cout<<" |";
} else {
if (i < 9) {
cout<<" 0";
} else {
cout<<" ";
}
cout<<i + 1<<" ";
}
for (int j = 0; j < 25; j++) {
if (i == cursor[0] && j == cursor[1] && isSetup) { //some bits that control how the cursor looks
if (playSpace[i][j]) {
cout<<"%";
} else {
cout<<"_";
}
} else if (playSpace[i][j]) { //draws differently based on each tile of the playSpace array
cout<<"#";
} else {
cout<<" ";
}
cout<<" ";
if (isSetup) cout<<" ";
}
if (!isSetup) cout<<"|";
cout<<"\n";
}
if (!isSetup) cout<<" |__________________________________________________|\n";
if (isSetup && lastInputBad) { //this is why we need that boolean to draw this message at the bottom of the screen
lastInputBad = false;
cout<<"Invalid input! Try again.";
}
}
void EvalCell(int i, int j) { //figures out if the cell at the input coords needs to change states
int rowAbove = j - 1;
int rowBelow = j + 1;
int colLeft = i - 1;
int colRight = i + 1;
int numNeighbors = 0;
if (rowAbove == -1) rowAbove = 24; //screen wrapping
if (rowBelow == 25) rowBelow = 0;
if (colLeft == -1) colLeft = 24;
if (colRight == 25) colRight = 0;
if (playSpace[colLeft][rowAbove]) {
numNeighbors++;
}
if (playSpace[i][rowAbove]) {
numNeighbors++;
}
if (playSpace[colRight][rowAbove]) {
numNeighbors++;
}
if (playSpace[colLeft][j]) {
numNeighbors++;
}
if (playSpace[colRight][j]) {
numNeighbors++;
}
if (playSpace[colLeft][rowBelow]) {
numNeighbors++;
}
if (playSpace[i][rowBelow]) {
numNeighbors++;
}
if (playSpace[colRight][rowBelow]) {
numNeighbors++;
}
if (playSpace[i][j] == 0 && numNeighbors == 3) { //store the eval results in nextGen
nextGen[i][j] = 1;
}
if (playSpace[i][j] == 1 && numNeighbors < 2) {
nextGen[i][j] = 0;
}
if (playSpace[i][j] == 1 && numNeighbors > 3) {
nextGen[i][j] = 0;
}
if (playSpace[i][j] == 1 && (numNeighbors == 2 || numNeighbors == 3)) {
nextGen[i][j] = 1;
}
}
void CalculateNextGen() {
totalLife = 0;
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
EvalCell(i, j); //evaluate all the cells
}
}
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
playSpace[i][j] = nextGen[i][j]; //set the current frame to be the calculated next frame
if (playSpace[i][j]) totalLife++; //figure out how many cells there are (and if there are 0 the program will end)
}
}
}
int main()
{
ResetPlaySpace();
cout<<"Welcome to Conway's Game of Life!\nHere you will be presented with a 25 by 25 board.\nOn this board, there are some simple rules!\nEach tile is called a \"cell\", and each cell can be either living (#) or dead ( )\nIf a living cell has 0 or 1 living cells near it, it dies on the next iteration.\nIf a living cell has 4 or more living cells near it, it also dies.\nIf a dead cell has exactly 3 living cells around it, it comes to life on the next iteration.\nIf none of these conditions are met, the cell stays as it was.\nPress enter to start!";
cin.get();
system("clear");
while(setup) {
DrawPlaySpace(true);
getline(cin, userInput);
if (userInput == "start") {
setup = false;
} else if (userInput == "w") {
cursor[0]--;
if (cursor[0] == -1) cursor[0] = 24;
} else if (userInput == "s") {
cursor[0]++;
if (cursor[0] == 25) cursor[0] = 0;
} else if (userInput == "a") {
cursor[1]--;
if (cursor[1] == -1) cursor[1] = 24;
} else if (userInput == "d") {
cursor[1]++;
if (cursor[1] == 25) cursor[1] = 0;
} else if (userInput == " ") {
playSpace[cursor[0]][cursor[1]] = !playSpace[cursor[0]][cursor[1]];
} else if (userInput == "presets") { //the presets system
ResetPlaySpace();
system("clear");
cout<<"Which preset would you like?\nEnter the letter to select it.\na) Glider Example\nb) Stable Bread Loaf\nc) Ticking Time Bomb\nd) Party Lines\ne) Randomized Layout\n";
getline(cin, presetSelection);
setup = false;
if (presetSelection == "a") { //each of these manually enables some cells to load the preset
playSpace[11][12] = true;
playSpace[12][13] = true;
playSpace[13][11] = true;
playSpace[13][12] = true;
playSpace[13][13] = true;
DrawPlaySpace(false);
cout<<" Would you like to use this preset? Y/N:\n";
getline(cin, confirmation);
if (confirmation == "y" || confirmation == "Y") {
cout<<" Beginning simulation...";
sleep_for(nanoseconds(1000000)); //THESE DON'T WORK
} else if (confirmation == "n" || confirmation == "N") {
cout<<" Cancelling...";
setup = true;
sleep_for(nanoseconds(1000000)); //NONE OF THEM
} else {
cout<<" Interpreting vague answer as: YES";
sleep_for(nanoseconds(1000000)); //WHYYYYYYYYYYYYY
}
} else if (presetSelection == "b") {
playSpace[11][13] = true;
playSpace[12][12] = true;
playSpace[12][14] = true;
playSpace[13][11] = true;
playSpace[13][14] = true;
playSpace[14][12] = true;
playSpace[14][13] = true;
DrawPlaySpace(false);
cout<<" Would you like to use this preset? Y/N:\n";
getline(cin, confirmation);
if (confirmation == "y" || confirmation == "Y") {
cout<<" Beginning simulation...";
sleep_for(nanoseconds(1000000));
} else if (confirmation == "n" || confirmation == "N") {
cout<<" Cancelling...";
setup = true;
sleep_for(nanoseconds(1000000));
} else {
cout<<" Interpreting vague answer as: YES";
sleep_for(nanoseconds(1000000));
}
} else if (presetSelection == "c") {
playSpace[0][0] = true;
playSpace[0][1] = true;
playSpace[1][0] = true;
playSpace[1][1] = true;
playSpace[3][4] = true;
playSpace[4][5] = true;
playSpace[5][3] = true;
playSpace[5][4] = true;
playSpace[5][5] = true;
DrawPlaySpace(false);
cout<<" Would you like to use this preset? Y/N:\n";
getline(cin, confirmation);
if (confirmation == "y" || confirmation == "Y") {
cout<<" Beginning simulation...";
sleep_for(nanoseconds(1000000));
} else if (confirmation == "n" || confirmation == "N") {
cout<<" Cancelling...";
setup = true;
sleep_for(nanoseconds(1000000));
} else {
cout<<" Interpreting vague answer as: YES";
sleep_for(nanoseconds(1000000));
}
} else if (presetSelection == "d") {
playSpace[0][12] = true;
playSpace[1][12] = true;
playSpace[2][12] = true;
playSpace[3][12] = true;
playSpace[4][12] = true;
playSpace[5][12] = true;
playSpace[6][12] = true;
playSpace[7][12] = true;
playSpace[8][12] = true;
playSpace[9][12] = true;
playSpace[10][12] = true;
playSpace[11][12] = true;
playSpace[12][12] = true;
playSpace[13][12] = true;
playSpace[14][12] = true;
playSpace[15][12] = true;
playSpace[16][12] = true;
playSpace[17][12] = true;
playSpace[18][12] = true;
playSpace[19][12] = true;
playSpace[20][12] = true;
playSpace[21][12] = true;
playSpace[22][12] = true;
playSpace[23][12] = true;
playSpace[24][12] = true;
playSpace[0][13] = true;
playSpace[1][13] = true;
playSpace[2][13] = true;
playSpace[3][13] = true;
playSpace[4][13] = true;
playSpace[5][13] = true;
playSpace[6][13] = true;
playSpace[7][13] = true;
playSpace[8][13] = true;
playSpace[9][13] = true;
playSpace[10][13] = true;
playSpace[11][13] = true;
playSpace[12][13] = true;
playSpace[13][13] = true;
playSpace[14][13] = true;
playSpace[15][13] = true;
playSpace[16][13] = true;
playSpace[17][13] = true;
playSpace[18][13] = true;
playSpace[19][13] = true;
playSpace[20][13] = true;
playSpace[21][13] = true;
playSpace[22][13] = true;
playSpace[23][13] = true;
playSpace[24][13] = true;
DrawPlaySpace(false);
cout<<" Would you like to use this preset? Y/N:\n";
getline(cin, confirmation);
if (confirmation == "y" || confirmation == "Y") {
cout<<" Beginning simulation...";
sleep_for(nanoseconds(1000000));
} else if (confirmation == "n" || confirmation == "N") {
cout<<" Cancelling...";
setup = true;
sleep_for(nanoseconds(1000000));
} else {
cout<<" Interpreting vague answer as: YES";
sleep_for(nanoseconds(1000000));
}
} else if (presetSelection == "e") { //randome, ooh fancy, spoiler alert it doesn't work
srand (time(NULL));
for (int i = 0; i < 25; i++) {
for (int j = 0; j < 25; j++) {
int randomCell = rand() % 1;
playSpace[i][j] = randomCell;
}
}
DrawPlaySpace(false);
cout<<" Would you like to use this preset? Y/N:\n";
getline(cin, confirmation);
if (confirmation == "y" || confirmation == "Y") {
cout<<" Beginning simulation...";
sleep_for(nanoseconds(1000000));
} else if (confirmation == "n" || confirmation == "N") {
cout<<" Cancelling...";
setup = true;
sleep_for(nanoseconds(1000000));
} else {
cout<<" Interpreting vague answer as: YES";
sleep_for(nanoseconds(1000000));
}
} else {
cout<<" Invalid input";
setup = true;
sleep_for(nanoseconds(1000000));
}
} else {
lastInputBad = true; //invalid input stuff yay
}
}
while (true) { //finally, the main loop
iterations++;
DrawPlaySpace(false);
CalculateNextGen();
RecordGen();
DetectStability();
if (lastFrame) break;
if (iterations >= 10 && (!totalLife || stableLock)) lastFrame = true;
/*if (stableLock) {
cout<<"\n STABLE";
} else {
cout<<"\n UNSTABLE";
}*/
//more of the broken stability stuff^
usleep(250000);
//cin.get();
//cin.ignore();
//debug stuff that makes frame progression manual, uncomment to use^
}
return iterations; //exits with how many gens it simulated for funnies
}
I’ve tried using 3 different methods of pausing the program: usleep();
, std::this_thread::whatever
, and sleep_for(nanoseconds());
. Nothing works, which makes me think it’s a me error, but last time I checked programs are read top down and my cout
line is above my delays every single time. And every time, without fail, it ouputs the text after waiting but before the next frame.
Better idea: Back up your program and hack it down to a tiny program that does nothing other than demonstrate the problem. Use minimal reproducible example for inspiration. You won’t have to comment much if the program is reduced to , say, 10 lines of code.
A side note on commenting: You should only need comments to provide a descriptive header at the top of functions, data structure definitions, important variables, and particularly thorny bits of code. The rest of the intent should be readable from the code itself. This usually means use good, descriptive names and don’t pack too much on a single line.
the output is probably line buffered, you’ll need to explicitly flush if you want output before the end of a line
std::flush
or usestd::endl
.Don’t be too put off by asking a duplicate. It’s really hard for anyone at a beginner level to ask a question in any established programming language that hasn’t already been asked and answered in the last 15 years. The real key is making sure you’ve isolated the problem. If you have the bug trapped in a small piece of code you’ve at least found the problem and sort-of know what you’re asking about. This makes it easier to find duplicates on your own, or solve the problem outright on your own, and not have to ask a question at all.
Show 7 more comments