C++ implementation of CGOL

Has something gone haywire? Let us know about it!
Post Reply
User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

C++ implementation of CGOL

Post by gameoflifemaniac » June 8th, 2020, 12:25 pm

This amateur pile of code behaves weirdly sometimes:

Code: Select all

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

main()
{
	int x,y;
	cout << "Choose grid dimensions (x,y):";
	cin >> x >> y;
	int current[x][y];
	int next[x][y];
	int neighbors;
	srand((int)time(0));
	for (int i = 0; i < y; i++)
		for (int j = 0; j < x; j++)
		{
			current[i][j]=(rand()%2);
		}
			
	while (true)
	{
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		cout << endl;
		for (int i=0;i<y;i++)
		{
			cout << "XX ";
			for (int j = 0;j < x; j++)
			{
				current[i][j] ? cout << " " : cout << "  ";
				neighbors = 0;
				for (int k = -1; k < 2; k++)
				{
					for (int l = -1; l++ < 2;)
					{
						if (k||l)
						neighbors+=current[(i+l)%y][(j+k)%x];
					}
				}
				if (current[i][j])
				{
					(neighbors == 2 || neighbors == 3) ? next[i][j] = 1 : next[i][j] = 0;
				}
				else
				{
					neighbors == 3 ? next[i][j] = 1 : next[i][j] = 0;
				}
			}
			cout << "XX";
			cout << endl;
		}
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		for (int i = 0; i < x; i++)
		{
			for (int j = 0; j < y; j++)
			{
				current[i][j] = next[i][j];
			}
		}
		sleep(0.5);
		system("cls");
	}
}
Sometimes it allows impossible still lives like the Z pentomino to live close to the edge. How can I fix it?
EDIT: Great. Now it went haywire completely.
I was so socially awkward in the past and it will haunt me for my entire life.

User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: C++ implementation of CGOL

Post by gameoflifemaniac » June 21st, 2020, 6:26 am

...
I was so socially awkward in the past and it will haunt me for my entire life.

User avatar
rowett
Moderator
Posts: 2073
Joined: January 31st, 2013, 2:34 am
Location: UK
Contact:

Re: C++ implementation of CGOL

Post by rowett » June 27th, 2020, 11:20 am

The % operator is not doing what you expect. When the value is negative it won't wrap it around. -1 % 20 is not 19, it's -1.

bubblegum
Posts: 181
Joined: August 25th, 2019, 11:59 pm
Location: click here to do nothing

Re: C++ implementation of CGOL

Post by bubblegum » June 30th, 2020, 1:29 am

rowett wrote:
June 27th, 2020, 11:20 am
The % operator is not doing what you expect. When the value is negative it won't wrap it around. -1 % 20 is not 19, it's -1.
Apparently it's not even consistent. Boo. Try this:

Code: Select all

int mod(int a, int b) {
	int s {(a > 0) - (a < 0)};
	a = ((s + 1) > 0) ? a : b + a;
	return a % b;
}
(excuse the horrible mess of simplified code)
bubblegum wrote:
July 2nd, 2020, 8:33 pm
conwaylife signatures are amazing[citation needed]

User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: C++ implementation of CGOL

Post by gameoflifemaniac » July 1st, 2020, 5:54 am

bubblegum wrote:
June 30th, 2020, 1:29 am
rowett wrote:
June 27th, 2020, 11:20 am
The % operator is not doing what you expect. When the value is negative it won't wrap it around. -1 % 20 is not 19, it's -1.
Apparently it's not even consistent. Boo. Try this:

Code: Select all

int mod(int a, int b) {
	int s {(a > 0) - (a < 0)};
	a = ((s + 1) > 0) ? a : b + a;
	return a % b;
}
(excuse the horrible mess of simplified code)
Actually I found a simpler way to do that, but was too lazy to implement it.
Just add 20 (if the grid's x size is 20 for example) to the x/y coordinates of the neighbors and then mod it.

Code: Select all

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

main()
{
	int x,y;
	cout << "Choose grid dimensions (x,y):";
	cin >> x >> y;
	int current[x][y];
	int next[x][y];
	int neighbors;
	srand((int)time(0));
	for (int i = 0; i < y; i++)
		for (int j = 0; j < x; j++)
		{
			current[i][j]=(rand()%2);
		}
			
	while (true)
	{
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		cout << endl;
		for (int i = 0; i++ < y;)
		{
			cout << "XX ";
			for (int j = 0; j++ < x;)
			{
				current[i][j] ? cout << " 0" : cout << "  ";
				neighbors = 0;
				for (int k = -1; k++ < 2;)
				{
					for (int l = -1; l++ < 2;)
					{
						if (k||l)
						neighbors+=current[(i+l+x)%x][(j+k+y)%y];
					}
				}
				if (current[i][j])
				{
					(neighbors == 2 || neighbors == 3) ? next[i][j] = 1 : next[i][j] = 0;
				}
				else
				{
					neighbors == 3 ? next[i][j] = 1 : next[i][j] = 0;
				}
			}
			cout << "XX";
			cout << endl;
		}
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		for (int i = 0; i < y; i++)
		{
			for (int j = 0; j < x; j++)
			{
				current[i][j] = next[i][j];
			}
		}
		sleep(0.5);
		system("cls");
	}
}
But still something's not right, because it went haywire completely!
I was so socially awkward in the past and it will haunt me for my entire life.

chris_c
Posts: 963
Joined: June 28th, 2014, 7:15 am

Re: C++ implementation of CGOL

Post by chris_c » July 2nd, 2020, 6:19 pm

gameoflifemaniac wrote:
July 1st, 2020, 5:54 am
But still something's not right, because it went haywire completely!
You should investigate the behaviour of these for loops:

Code: Select all

				for (int k = -1; k++ < 2;)
				{
					for (int l = -1; l++ < 2;)
					{
						if (k||l)
						neighbors+=current[(i+l+x)%x][(j+k+y)%y];
					}
				}
e.g. add something like:

Code: Select all

std::cout << k << " " << l << std::endl;
to the inner loop. I doubt the result is what you intended.

User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: C++ implementation of CGOL

Post by gameoflifemaniac » July 3rd, 2020, 3:38 pm

chris_c wrote:
July 2nd, 2020, 6:19 pm
gameoflifemaniac wrote:
July 1st, 2020, 5:54 am
But still something's not right, because it went haywire completely!
You should investigate the behaviour of these for loops:

Code: Select all

				for (int k = -1; k++ < 2;)
				{
					for (int l = -1; l++ < 2;)
					{
						if (k||l)
						neighbors+=current[(i+l+x)%x][(j+k+y)%y];
					}
				}
e.g. add something like:

Code: Select all

std::cout << k << " " << l << std::endl;
to the inner loop. I doubt the result is what you intended.
I've fixed it. Kind of. It behaves more like CGoL but still allows for impossible stuff on the edges.

Code: Select all

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

main()
{
	int x,y;
	cout << "Choose grid dimensions (x,y):";
	cin >> x >> y;
	int current[x][y];
	int next[x][y];
	int neighbors;
	srand((int)time(0));
	for (int i = 0; i < y; i++)
		for (int j = 0; j < x; j++)
			current[i][j]=(rand()%2);
	while (true)
	{
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		cout << endl;
		for (int i = 0; i++ < y;)
		{
			cout << "XX ";
			for (int j = 0; j++ < x;)
			{
				current[i][j] ? cout << " 0" : cout << "  ";
				neighbors = 0;
				
				for (int deltay = -1; deltay < 2; deltay++)
					for (int deltax = -1; deltax < 2; deltax++)
						if (deltax||deltay)
							neighbors+=current[(i+deltax+x)%x][(j+deltay+y)%y];
				if (current[i][j])
					(neighbors == 2 || neighbors == 3) ? next[i][j] = 1 : next[i][j] = 0;
				else
					neighbors == 3 ? next[i][j] = 1 : next[i][j] = 0;
			}
			cout << "XX";
			cout << endl;
		}
		for (int a = 0; a++ < 2*x + 5;)
		cout << "X";
		for (int i = 0; i++ < y;)
			for (int j = 0; j++ < x;)
				current[i][j] = next[i][j];
		sleep(0.5);
		system("cls");
	}
}
Also how is:

Code: Select all

for (int deltax = -1; deltax++ < 2;)
any different from

Code: Select all

for (int deltax = -1; deltax < 2; deltax++)
?
I've reverted the 2nd version back and it worked correctly. How?
I was so socially awkward in the past and it will haunt me for my entire life.

chris_c
Posts: 963
Joined: June 28th, 2014, 7:15 am

Re: C++ implementation of CGOL

Post by chris_c » July 3rd, 2020, 4:15 pm

gameoflifemaniac wrote:
July 3rd, 2020, 3:38 pm
Also how is:

Code: Select all

for (int deltax = -1; deltax++ < 2;)
any different from

Code: Select all

for (int deltax = -1; deltax < 2; deltax++)
?
I've reverted the 2nd version back and it worked correctly. How?
As explained here. The structure of a for loop in C++ is:

Code: Select all

for (init-statement; condition; iteration_expression)
The init-statement is always executed at the beginning.

The condition is evaluated before every loop. If the condition is non-zero then loop executes, otherwise the for loop ends.

The iteration_expression is evaluated at the end of every loop. (If the condition is 0 to begin with then the iteration_expression is never evaluated.)

Therefore in your original method the variable was being incremented before every loop but the second for is incrementing after every loop.

Off the top of my head I can't think any good examples where the condition statement would have a side effect such as incrementing a variable so I would avoid doing that kind of thing.

User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: C++ implementation of CGOL

Post by gameoflifemaniac » July 3rd, 2020, 4:20 pm

chris_c wrote:
July 3rd, 2020, 4:15 pm
gameoflifemaniac wrote:
July 3rd, 2020, 3:38 pm
Also how is:

Code: Select all

for (int deltax = -1; deltax++ < 2;)
any different from

Code: Select all

for (int deltax = -1; deltax < 2; deltax++)
?
I've reverted the 2nd version back and it worked correctly. How?
As explained here. The structure of a for loop in C++ is:

Code: Select all

for (init-statement; condition; iteration_expression)
The init-statement is always executed at the beginning.

The condition is evaluated before every loop. If the condition is non-zero then loop executes, otherwise the for loop ends.

The iteration_expression is evaluated at the end of every loop. (If the condition is 0 to begin with then the iteration_expression is never evaluated.)

Therefore in your original method the variable was being incremented before every loop but the second for is incrementing after every loop.

Off the top of my head I can't think any good examples where the condition statement would have a side effect such as incrementing a variable so I would avoid doing that kind of thing.
Thanks for the explanation! But I still can't figure out how to make it work perfectly.

Code: Select all

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

main()
{
	int x,y;
	cout << "Choose grid dimensions (x,y):";
	cin >> x >> y;
	int current[x][y];
	int next[x][y];
	int neighbors;
	srand((int)time(0));
	for (int i = 0; i < y; i++)
		for (int j = 0; j < x; j++)
			current[i][j]=(rand()%2);
	while (true)
	{
		for (int a = 0; a < 2*x + 5; a++)
		cout << "X";
		cout << endl;
		for (int i = 0; i < y; i++)
		{
			cout << "XX";
			for (int j = 0; j < x; j++)
			{
				current[i][j] ? cout << " 0" : cout << "  ";
				neighbors = 0;
				
				for (int deltay = -1; deltay < 2; deltay++)
					for (int deltax = -1; deltax < 2; deltax++)
						if (deltax||deltay)
							neighbors+=current[(i+deltax+x)%x][(j+deltay+y)%y];
				if (current[i][j])
					(neighbors == 2 || neighbors == 3) ? next[i][j] = 1 : next[i][j] = 0;
				else
					neighbors == 3 ? next[i][j] = 1 : next[i][j] = 0;
			}
			cout << " XX";
			cout << endl;
		}
		for (int a = 0; a < 2*x + 5; a++)
		cout << "X";
		for (int i = 0; i < y; i++)
			for (int j = 0; j < x; j++)
				current[i][j] = next[i][j];
		system("cls");
	}
}
Ok I've changed all the loop conditions and it seems to work now.
But there still is a problem when inputting grid sizes with a height larger than the width. Sometimes it makes super tiny grids, sometimes it works normal, and sometimes it crashes. Why?
I was so socially awkward in the past and it will haunt me for my entire life.

chris_c
Posts: 963
Joined: June 28th, 2014, 7:15 am

Re: C++ implementation of CGOL

Post by chris_c » July 3rd, 2020, 4:57 pm

gameoflifemaniac wrote:
July 3rd, 2020, 4:20 pm
Ok I've changed all the loop conditions and it seems to work now.
But there still is a problem when inputting grid sizes with a height larger than the width. Sometimes it makes super tiny grids, sometimes it works normal, and sometimes it crashes. Why?
I think I see the reason. You define your arrays like:

Code: Select all

int array[x][y]
And access them like:

Code: Select all

array[i][j]
Therefore you need to make sure that i runs between 0 and x-1, and that j runs between 0 and y-1. Looks like you have this the wrong way round so will only work currently if x and y happen to be the same.

User avatar
gameoflifemaniac
Posts: 1096
Joined: January 22nd, 2017, 11:17 am
Location: There too

Re: C++ implementation of CGOL

Post by gameoflifemaniac » July 3rd, 2020, 5:14 pm

chris_c wrote:
July 3rd, 2020, 4:57 pm
gameoflifemaniac wrote:
July 3rd, 2020, 4:20 pm
Ok I've changed all the loop conditions and it seems to work now.
But there still is a problem when inputting grid sizes with a height larger than the width. Sometimes it makes super tiny grids, sometimes it works normal, and sometimes it crashes. Why?
I think I see the reason. You define your arrays like:

Code: Select all

int array[x][y]
And access them like:

Code: Select all

array[i][j]
Therefore you need to make sure that i runs between 0 and x-1, and that j runs between 0 and y-1. Looks like you have this the wrong way round so will only work currently if x and y happen to be the same.

Code: Select all

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

main()
{
	int x,y;
	cout << "Choose grid dimensions (x,y):";
	cin >> x >> y;
	int current[x][y];
	int next[x][y];
	int neighbors;
	srand((int)time(0));
	for (int i = 0; i < x; i++)
		for (int j = 0; j < y; j++)
			current[i][j]=(rand()%2);
	while (true)
	{
		for (int a = 0; a < 2*y + 5; a++)
		cout << "X";
		cout << endl;
		for (int i = 0; i < x; i++)
		{
			cout << "XX";
			for (int j = 0; j < y; j++)
			{
				current[i][j] ? cout << " 0" : cout << "  ";
				neighbors = 0;
				
				for (int deltay = -1; deltay < 2; deltay++)
					for (int deltax = -1; deltax < 2; deltax++)
						if (deltax||deltay)
							neighbors+=current[(i+deltax+x)%x][(j+deltay+y)%y];
				if (current[i][j])
					(neighbors == 2 || neighbors == 3) ? next[i][j] = 1 : next[i][j] = 0;
				else
					neighbors == 3 ? next[i][j] = 1 : next[i][j] = 0;
			}
			cout << " XX";
			cout << endl;
		}
		for (int a = 0; a < 2*y + 5; a++)
		cout << "X";
		for (int i = 0; i < x; i++)
			for (int j = 0; j < y; j++)
				current[i][j] = next[i][j];
		system("cls");
	}
}
I've switched x and y in the right places and it works perfectly. Thank you, bubblegum and rowett for your help!
I wanted to make this program to introduce myself into the programming language (it's only displayed in the console after all!). You taught me stuff I didn't learn from my online course, too.
I was so socially awkward in the past and it will haunt me for my entire life.

Post Reply