Page 1 of 1

Game of Life in Console

#1 SixOfEleven  Icon User is offline

  • using Caffeine;
  • member icon

Reputation: 944
  • View blog
  • Posts: 6,342
  • Joined: 18-October 08

Post icon  Posted 09 April 2009 - 06:32 PM

In the C# Programming help topic there was a request made for programming the Game of Life in a GUI interface. The Game of Life was origanlly called cellular automaton and was made popular in 1970 in an article in Scientific America

The concept was that you have a field of cells that were either alive or dead, in the origanal version the field was infinite. Each generation the field was iterated and rules were applied to the field. These were rules:

If a cell was alive and had one or no neighbours the cell died (as if from lonliness)
If a cell was alive and had two or three neighbours the cell would survive.
If a cell was alive and had four or more neighbours it would die (from over population)
If there was a dead cell that had exactly three neighbours a new cell would be born

A neighbour was considered to be any of the eight adjacent cells. Think of the number pad on your computer. The 1, 2, 3, 4, 6, 7, 8 and 9 keys would be neighbours of the 5 key.

I made a class, in console mode, that followed these rules. The class would iterate over a field and post the results for each new generation. Since this is in Console mode I made it so that after each iteration the user would have to press enter to continue or n and enter to quit. The program would also terminate if all cells died.

To make this project load your version C#, I used C# Express 2008, and create a Console Application. Add a new class to the project. I called it LifeGrid. To this class add five variables:

int[,] generation;
int[,] lastGeneration;


int generationCount;

int width;
int height;



The first holds the state of the current generation of cells. The second holds the state of the last generation of cells. Next is counter that holds the number of generations that have been iterated. The last two hold the width and the height of the generation. I added these because you frequently need to know the outer edges of the grid while checking for neighbours and the size of the grid is up to the user. I don't know how you could go about implenting an infinite sized grid. So I made the assumption that many others who have programmed the Game of Life that anything outside of the grid passed by the user is dead and does not need to be processed.

Since the count variable is private I added a property to allow the user to get the current generation. It is just a get because there is no need for the user to specifiy what the current generation is. This is the code for the property:

public int GenerationCount
{
	 get { return generationCount; }
}



Next I programmed the constuctor. I made it so that the user would have to send a 2-dimensional array of integers. A 1 would indicate a live cell and 0 would indicate a dead cell.

This is the code for the constructor:

		public LifeGrid(int[,] newGrid)
		{
			generation = (int[,])newGrid.Clone();

			generationCount = 1;

			width = generation.GetLength(1);
			height = generation.GetLength(0);

			lastGeneration = new int[height, width];
		}



After reading this you might be screaming that width and height are in the wrong order! In math coordinates are usually written as (x,y). I don't know why but they are reversed here, they are (y,x). Otherwise everything you do will be at a 90 degree angel and nothing will work the way it should.

So what does the constructor do? Well, first it creates the generation by cloning the array, I call it generation 0. It sets the counter for the first generation. Then it stores the width and height of the grid using the GetLength method of the array. Then it creates an empty grid that will be used to store the state of the last geneartion.

To help in processing the generation I created a Neighbours method that is passed the coordinates of the cell and count the number of neighbours the cell has. I put the coordinates in (x,y) format. Here is the code for the Neighbours method:

		private int Neighbours(int x, int y)
		{
			int count = 0;

			// Check for x - 1, y - 1
			if (x > 0 && y > 0)
			{
				if (generation[y - 1, x - 1] == 1)
					count++;
			}

			// Check for x, y - 1
			if (y > 0)
			{
				if (generation[y - 1, x] == 1)
					count++;
			}

			// Check for x + 1, y - 1
			if (x < width - 1 && y > 0)
			{
				if (generation[y - 1, x + 1] == 1)
					count++;
			}

			// Check for x - 1, y
			if (x > 0)
			{
				if (generation[y, x - 1] == 1)
					count++;
			}

			// Check for x + 1, y
			if (x < width - 1)
			{
				if (generation[y, x + 1] == 1)
					count++;
			}

			// Check for x - 1, y + 1
			if (x > 0 && y < height - 1)
			{
				if (generation[y + 1, x - 1] == 1)
					count++;
			}

			// Check for x, y + 1
			if (y < height - 1)
			{
				if (generation[y + 1, x] == 1)
					count++;
			}

			// Check for x + 1, y + 1
			if (x < width - 1 && y < height - 1)
			{
				if (generation[y + 1, x + 1] == 1)
					count++;
			}

			return count;
		}



All the code does is create a variable to hold the number of neighbours the cell has and then checks each neighbour, making sure that a neighbour that is outside of the grid is not checked. In order the following coordinates are checked (x - 1, y - 1) (x, y - 1), (x + 1, y - 1), (x - 1, y), (x + 1, y), (x - 1, y + 1), (x, y + 1) and (x + 1, y + 1). I put in comments to show which neighbour is being checked.

I put in a public method to check the number of neighbours each cell has and display the results to the console window, just to make sure that the Neighbours method worked. I've included it so you can try it to see that using (y, x) instead of (x, y) was the right choice. All the method does is iterate over each cell and display the number of neighbours. This is the code:

		public void WriteNeighbours()
		{
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
					Console.Write("{0}", Neighbours(x, y));
				Console.WriteLine();
			}
		}



Now comes the heart of the class, the ProcessGeneration method. I will show you the code then explain it:

public void ProcessGeneration()
{
	int[,] nextGeneration = new int[height, width];

	lastGeneration = (int[,])generation.Clone();

	generationCount++;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			if (Neighbours(x, y) < 2)
				nextGeneration[y, x] = 0;
			else if (generation[y, x] == 0 && Neighbours(x, y) == 3)
				nextGeneration[y, x] = 1;
			else if (generation[y, x] == 1 && 
					(Neighbours(x, y) == 2 || Neighbours(x, y) == 3))
				nextGeneration[y, x] = 1;
			else
				nextGeneration[y, x] = 0;
		}
	}

	generation = (int[,])nextGeneration.Clone();
}



First I created a variable to hold the next state. Then I set the last generation to a Clone of the current generation. I used Clone because if I did an assignment and changed one it would change the other. By using two nested for loops I iterated each cell in the grid. Then I used a series of if statements that are linked together with elses. The first one checks to see if the cell has 0 or 1 neighbours, if it does the cell is killed by setting it to 0. The next one checks to see if the cell is dead and has three neighbours. If it does a new cell is born. The next one checks to see if the cell is alive and it has two or three neighbours. If that is true the cell is alive. In all other cases the cell is dead. Thinking about it now I think the code may be optimized. All you really have to do is just assign the 1's since when the array was created it was filled to 0. So you can replace that complicated mess with just two if statements. Here is the revised code:

public void ProcessGeneration()
{
	int[,] nextGeneration = new int[height, width];

	lastGeneration = (int[,])generation.Clone();

	generationCount++;

	for (int y = 0; y < height; y++)
	{
		for (int x = 0; x < width; x++)
		{
			if (generation[y, x] == 0 && Neighbours(x, y) == 3)
				nextGeneration[y, x] = 1;
			if (generation[y, x] == 1 && 
				   (Neighbours(x, y) == 2 || Neighbours(x, y) == 3))
				nextGeneration[y, x] = 1;
		}
	}

	generation = (int[,])nextGeneration.Clone();
}



Looks a lot cleaner doesn't it?

I added two more methods to the class. One to write the generation to the screen and one to count the number of cells that are alive.

Both of them simply iterate over the current generation. In the draw method each cell is written row by row with a WriteLine after each row. In the other method it just adds one to the count if there is a cell that is alive and returns the value. Here are the methods:

		public void DrawGeneration()
		{
			for (int y = 0; y < height; y++)
			{
				for (int x = 0; x < width; x++)
					Console.Write("{0}", generation[y, x]);
				Console.WriteLine();
			}
			Console.WriteLine();
		}

		public int AliveCells()
		{
			int count = 0;

			for (int y = 0; y < height; y++)
				for (int x = 0; x < width; x++)
					if (generation[y, x] == 1)
						count++;

			return count;
		}



Now it is time to test the class. I will be doing everything in the Main method because all I want to do is test the class. So go to the Program.cs file and go to the Main method. This is the Main method that I used, I will explain it after you see the code.

static void Main(string[] args)
{
	int[,] grid = new int[,]
	{
		{ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1,},
		{ 0, 0, 1, 0, 0, 1, 0, 0, 0, 0,},
		{ 0, 1, 1, 0, 1, 1, 1, 0, 0, 0,},
		{ 0, 1, 0, 0, 0, 1, 0, 0, 0, 0,},
		{ 0, 0, 0, 0, 0, 0, 0, 1, 0, 1,},
		{ 0, 0, 0, 0, 1, 0, 0, 0, 1, 1,},
		{ 0, 1, 0, 0, 1, 0, 0, 1, 0, 0,},
		{ 1, 1, 0, 0, 1, 0, 0, 0, 0, 0,},
	};

	LifeGrid lifeGrid = new LifeGrid(grid);

	Console.WriteLine("Generation 0");
	lifeGrid.DrawGeneration();
	Console.WriteLine();

	while (lifeGrid.AliveCells() > 0)
	{
		string response;

		Console.WriteLine();
		Console.WriteLine("Generation {0}", lifeGrid.GenerationCount);

		lifeGrid.ProcessGeneration();
		lifeGrid.DrawGeneration();

		Console.WriteLine();

		if (lifeGrid.AliveCells() == 0)
		{
			Console.WriteLine("Every one died!");
			Console.ReadLine();
		}
		else
		{
			Console.WriteLine("Press <Enter> to contiune or n<Enter> to quit.");

			response = Console.ReadLine();

			if (response == "n" || response == "N")
				break;
		}
	}
}



To start you will want to make a 2 dimensional array that you can pass it to the object you will create. I tried to make a grid that would test all of the rules. If you let the program run through it will run to generation 15 where all cells are dead.

After you create the grid you want to use you create the object. After creating the object I write out Generation 0 and the grid using the method I created in the class.

Then I used a while loop that will run while there are cells that are alive. Next I created a string to hold the response from a ReadLine method of the Console object. I write a blank line and the current generation number. Then I process the generation and write out the new generation. Write a blank line. If there are no cells left alive I write a message and wait for the user to press Enter. If there are cells left, I write a prompt and wait for the user to enter something. If they type n or N I break from the loop.

That is the way the Game of Life works.

Is This A Good Question/Topic? 0
  • +

Page 1 of 1