Game of life

Generations aren't created properly

Page 1 of 1

4 Replies - 2264 Views - Last Post: 27 September 2009 - 01:31 PM Rate Topic: -----

#1 kira89  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 61
  • Joined: 27-November 07

Game of life

Posted 26 September 2009 - 10:21 AM

Hello,

I am creating a program that runs the game of life. An array represents a group of live (+) or dead (0) cells. The following transitions occur simultaneously on every cell for each generation:

1. Any live cell with fewer than 2 live neighbors dies (underpopulation)
2. Any live cell with greater than 2 live neighbors dies (overpopulation)
3. Any live cell with exactly 2 or 3 live neighbors lives to next generation
4. Any dead cell with exactly 3 live neighbors becomes a live cell.

I can't figure out why my generations aren't being created properly. They should appear as follows:

input: (5 rows, 5 columns, 3 generations)
00000
0+++0
0+0+0
0+++0
00000

00+00
0+0+0
+000+
0+0+0
00+00

00+00
0+++0
++0++
0+++0
00+00

0+++0
+000+
+000+
+000+
0+++0

Any insight would be great. Thanks!

My code is below.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Life
{
	class LifeGame
	{
		static void Main(string[] args)
		{
			(new LifeGame()).startMenu();
		}

		public void startMenu()
		{
			//input information
			Console.WriteLine("Intput number of columns: ");
			String input = Console.ReadLine();
			int columns = int.Parse(input);
			Console.WriteLine("Input number of rows: ");
			input = Console.ReadLine();
			int rows = int.Parse(input);
			Console.WriteLine("Input number of generations: ");
			input = Console.ReadLine();
			int generations = int.Parse(input);
			char[,] cells = new char[columns, rows];
			//create grid
			Table game = new Table(columns, rows, cells);
			//run game
			cells = game.playGame(cells, generations);
			//print outcome
			game.printTable(cells);
		}
	}

	public class Table
	{
		int columns, rows;
		public Table(int c, int r, char[,] cells)
		{
			columns = c;
			rows = r;
			Console.WriteLine("Input status grid: ");
			String current = Console.ReadLine();
			for (int i = 0; i < rows; i++)
			{
				for (int j = 0; j < columns; j++)
				{
					cells[i, j] = current[j];
				}
				current = Console.ReadLine();
			}
			//Console.WriteLine("The original seed is:");
			//printTable(cells);
		}

		public void printTable(char[,] array)
		{
			for (int i = 0; i < rows; i++)
			{
				for (int j = 0; j < columns; j++)
				{
					Console.Write(array[i, j]);
				}
				Console.WriteLine();
			}
			Console.WriteLine();
		}

		//public void changeStatus(char[,] array, int r, int c, char status)
		//{
		//	array[r, 0] = status;
		//}

		public char[,] playGame(char[,] array, int generations)
		{			
			int currentGen = generations;
			//create copy of input array
			char[,] newCells = new char[columns, rows];
			newCells = array;
			if (currentGen > 0)
			{
				//loop through all cells and change status as necessary
				for (int i = 0; i < rows; i++)
				{
					for (int j = 0; j < columns; j++)
					{
						int numLiveCells = 0;
						//if cell is alive
						if (array[i, j].Equals('+'))
						{
							//check number of live cells
							numLiveCells = countLiveCells(array, i, j);
							if ((numLiveCells < 2) || (numLiveCells > 3))
								//cell dies
								newCells[i, j] = '0';
							else
								//cell lives
								newCells[i, j] = '+';
						}
						//if cell is dead
						else
						{
							//check number of live cells
							numLiveCells = countLiveCells(array, i, j);
							if (numLiveCells == 3)
								//cell reincarnates
								newCells[i, i] = '+';
							else
								//cell stays dead
								newCells[i, i] = '0';
						}
					}	  
				}
				//print created generation
				printTable(newCells);
				//decrease generation
				currentGen--;
				//create next generation
				newCells = playGame(newCells, currentGen);
			}
			return array; 
		}

		public int countLiveCells(char[,] array, int i, int j)
		{
			/*loop through all surrounding cells and count live ones
			 * there is probably a better way to do this but I couldn't think of one.*/
			int number = 0;
			//check W
			j--;
			number += checkLive(array, i, j);
			//check NW
			i--;
			number += checkLive(array, i, j);
			//check N
			j++;
			number += checkLive(array, i, j);
			//check NE
			j++;
			number += checkLive(array, i, j);
			//check E
			i++;
			number += checkLive(array, i, j);
			//Check SE
			i++;
			number += checkLive(array, i, j);
			//check S
			j--;
			number += checkLive(array, i, j);
			//check SW
			j--;
			number += checkLive(array, i, j);
			//return number of live cells
			return number;
		}

		public int checkLive(char[,] array, int i, int j)
		{
			//if cell does not exist, return zero
			if ((i < 0) || (j < 0) || (i >= array.GetUpperBound(0)) || (j >= array.GetUpperBound(1)))
				return 0;
			//otherwise, if cell is alive, return 1; if dead, return 0
			else if (array[i, j].Equals('+'))
				return 1;
			else
				return 0;
		}
	}
}



Is This A Good Question/Topic? 0
  • +

Replies To: Game of life

#2 SixOfEleven  Icon User is offline

  • Planeswalker
  • member icon

Reputation: 1055
  • View blog
  • Posts: 6,643
  • Joined: 18-October 08

Re: Game of life

Posted 26 September 2009 - 12:57 PM

I have a tutorial here on DIC about making the Game of Life in Console.

But I will help you with your code. The first problem and you are going to find this really peculiar is that when you are processing the grid of cells the X and Y are reversed. You are using:

char[,] grid = new char[numRows, numColumns];

for (i = 0; i < numRows; i++)
	for (j = 0; j < numColumns; j++)
		Console.Write("{0}", grid[i,j]);



You have to do it reversed! grid[j, i].

char[,] grid = new char[numColumns, numRows];

for (j = 0; j < numColumns; j++)
	for (i = 0; i < numRows; i++)
		Console.Write("{0}", grid[j,i]);



This is a peculiarity when you are processing 2D arrays in C# and displaying them in a grid or in a tile map for example.

Another problem you are having is in this line of code:

newCells = array;



When you do a straight assignment like that you are actually modifying the original because arrays are reference types. Reference type means that if you have two objects of the same class we will call them A and B and you assign A to B and you change something in B it changes in A as well. Fortunately there is an easy way to fix this. System.Array has a method called Clone. You can use that method to clone the array and work on a copy of it.

char[,] newCells = (char[,])array.Clone();



That creates a copy of the array and assigns it to newCells so you are now working on a copy instead of the original. I suggest you read my tutorial on this. I think it will help you out a lot. :)

*edit* forgot to cast the array.

This post has been edited by SixOfEleven: 26 September 2009 - 01:00 PM

Was This Post Helpful? 0
  • +
  • -

#3 kira89  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 61
  • Joined: 27-November 07

Re: Game of life

Posted 27 September 2009 - 04:43 AM

Thanks! I looked at your suggestions and the tutorial, and it fixed some of the problem. The first generation is created properly but all the subsequent ones are wrong. I haven't been able to figure out why.

Here's my new code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Life
{
	class LifeGame
	{
		static void Main(string[] args)
		{
			(new LifeGame()).startMenu();
		}

		public void startMenu()
		{
			//input information
			Console.WriteLine("Intput number of columns: ");
			String input = Console.ReadLine();
			int columns = int.Parse(input);
			Console.WriteLine("Input number of rows: ");
			input = Console.ReadLine();
			int rows = int.Parse(input);
			Console.WriteLine("Input number of generations: ");
			input = Console.ReadLine();
			int generations = int.Parse(input);
			//Console.WriteLine("The number of generations is: " + generations);
			char[,] cells = new char[columns, rows];
			//create grid
			Table game = new Table(columns, rows, cells);
			//run game
			cells = game.playGame(cells, generations, 0);
			//print outcome
			Console.WriteLine("The final generation is: ");
			game.printTable(cells);
		}
	}

	public class Table
	{
		int columns, rows;
		public Table(int c, int r, char[,] cells)
		{
			columns = c;
			rows = r;
			Console.WriteLine("Input status grid: ");
			String current = Console.ReadLine();
			for (int j = 0; j < columns; j++)
			{
				for (int i = 0; i < rows; i++)
				{
					cells[j, i] = current[i];
				}
				current = Console.ReadLine();
			}
			Console.WriteLine("The original seed is:");
			printTable(cells);
		}

		public void printTable(char[,] array)
		{
			for (int j = 0; j < columns; j++)
			{
				for (int i = 0; i < rows; i++)
				{
					Console.Write(array[j, i]);
				}
				Console.WriteLine();
			}
			Console.WriteLine();
		}

		public char[,] playGame(char[,] array, int generations, int c)
		{
			int currentGen = generations;
			int count = c + 1;
			//create copy of input array
			char[,] newCells = new char[columns, rows];
			char[,] currentCells = (char[,])array.Clone();
			while (currentGen > 0)
			{
				newCells = (char[,])currentCells.Clone();
				//loop through all cells and change status as necessary
				for (int j = 0; j < columns; j++)
				{
					for (int i = 0; i < rows; i++)
					{
						int numLiveCells = 0;
						//if cell is alive
						if (currentCells[j, i].Equals('+'))
						{
							//check number of live cells
							numLiveCells = countLiveCells(currentCells, j, i);
							if ((numLiveCells < 2) || (numLiveCells > 3))
								//cell dies
								newCells[j, i] = '0';
							else
								//cell lives
								newCells[j, i] = '+';
						}
						//if cell is dead
						else
						{
							//check number of live cells
							numLiveCells = countLiveCells(currentCells, j, i);
							if (numLiveCells == 3)
								//cell reincarnates
								newCells[j, i] = '+';
							else
								//cell stays dead
								newCells[j, i] = '0';
						}
					}
				}
				//print created generation
				Console.WriteLine("Generation " + count + " is: "); 
				printTable(newCells);
				//decrease generation
				currentGen--;
				count++;
				currentCells = (char[,])newCells.Clone();
				//create next generation
				//currentCells = playGame(newCells, currentGen, count);
			}
			return newCells;
		}

		public int countLiveCells(char[,] array, int j, int i)
		{
			//loop through all surrounding cells and count live ones
			int number = 0;
			// Check for x - 1, y - 1
			number += checkLive(array, j - 1, i - 1);
			// Check for x, y - 1
			number += checkLive(array, j - 1, i);
			// Check for x + 1, y - 1
			number += checkLive(array, j - 1, i + 1);
			// Check for x - 1, y
			number += checkLive(array, j, i - 1);
			// Check for x + 1, y
			number += checkLive(array, j, i + 1);
			// Check for x - 1, y + 1
			number += checkLive(array, j + 1, i - 1);
			// Check for x, y + 1
			number += checkLive(array, j + 1, i);
			// Check for x + 1, y + 1
			number += checkLive(array, j + 1, i + 1);
			// return number of live cells
			return number;
		}

		public int checkLive(char[,] array, int j, int i)
		{
			//if cell does not exist, return zero
			if ((i < 0) || (j < 0) || (j >= array.GetUpperBound(0)) || (i >= array.GetUpperBound(1)))
				return 0;
			//otherwise: if cell is alive, return 1; if dead, return 0
			else if (array[j, i].Equals('+'))
				return 1;
			else
				return 0;
		}
	}
}


Was This Post Helpful? 0
  • +
  • -

#4 baavgai  Icon User is offline

  • Dreaming Coder
  • member icon


Reputation: 6996
  • View blog
  • Posts: 14,635
  • Joined: 16-October 07

Re: Game of life

Posted 27 September 2009 - 09:59 AM

Your code is horribly inconsistent in how it addresses which cells are which, but I can't see it. I loaded your code up and then proceeded to compulsively "fix" it.

My big problem with the code is that Table doesn't really do anything, it's just a method repository. It should be nice, abstract representation of a grid of cells. You're also getting confused by generation. You need only be concerned with a next generation, the rest will fall into place. I didn't like the char for cells; it need only be boolean. The char you intend to use should be a constant, never referenced directly.

Here's what I did. Read it over. I conformed to what you had, where possible, but it's still drastically different.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Life {
    class LifeGame {
        static void Main(string[] args) {
            //new LifeGame().startMenu();
            new LifeGame().runTest();
            Console.ReadLine();
        }

        public void startMenu() {
            Console.WriteLine("Intput number of columns: ");
            int columns = int.Parse(Console.ReadLine());
            Console.WriteLine("Input number of rows: ");
            int rows = int.Parse(Console.ReadLine());
            Console.WriteLine("Input number of generations: ");
            int generations = int.Parse(Console.ReadLine());

            Table table = new Table(rows, columns);
            userEntry(table);
            playGame(table, generations);
        }



        private void playGame(Table table, int generations) {
            Console.WriteLine("input: (" + table.Rows + " rows, " + table.Rows + " columns, " + generations + " generations)");
            Console.WriteLine("The original seed is: \n" + table);
            for (int i = 1; i < generations+1; i++) {
                table = table.getNextGeneration();
                Console.WriteLine("Generation " + i + " is: \n" + table);
            }
        }

        public void runTest() {
            List<string> list = new List<string>(new string[] {
                "00000","0+++0","0+0+0","0+++0","00000"
            });
            Table table = new Table(5,5);
            loadStrings(table, list);
            playGame(table, 3);
        }


        private void loadStrings(Table table, List<string> list) {
            for (int row = 0; row < table.Rows; row++) {
                string current = list[row];
                for (int col = 0; col < table.Cols; col++) {
                    table.setCharValue(row, col, current[col]);
                }
            }
        }

        private void userEntry(Table table) {
            List<string> list = new List<string>();
            Console.WriteLine("Input status grid: ");
            for (int row = 0; row < table.Rows; row++) {
                list.Add(Console.ReadLine());
            }
            loadStrings(table, list);
        }
    }


    public class Table {
        public static readonly char CELL_ALIVE = '+';
        public static readonly char CELL_DEAD = '0';
        //int columns, rows;
        private readonly int rows, cols;
        private bool[,] cells;

        public Table(int rows, int cols) {
            this.rows = rows;
            this.cols = cols;
            this.cells = new bool[rows, cols];
            //print();
        }

        private Table(Table other) {
            this.rows = other.rows;
            this.cols = other.cols;
            this.cells = new bool[rows, cols];
        }

        public int Rows { get { return rows; } }
        public int Cols { get { return cols; } }
        public bool getValue(int row, int col) { return this.cells[row, col]; }
        public void setValue(int row, int col, bool value) { this.cells[row, col] = value; }

        public char getCharValue(int row, int col) { 
            return getValue(row, col) ? Table.CELL_ALIVE : Table.CELL_DEAD;
        }
        public void setCharValue(int row, int col, char value) { 
            setValue(row, col, value==Table.CELL_ALIVE); 
        }


        public override string ToString() {
            string s = "";
            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
                    s += getCharValue(row, col);
                }
                s += "\n";
            }
            return s;
        }


        public Table getNextGeneration() {
            Table next = new Table(this);
            for (int row = 0; row < rows; row++) {
                for (int col = 0; col < cols; col++) {
                    int numLiveCells = countLiveCells(row, col);
                    if (getValue(row, col)) {
                        //1. Any live cell with fewer than 2 live neighbors dies (underpopulation)
                        //2. Any live cell with greater than 2 live neighbors dies (overpopulation)
                        //3. Any live cell with exactly 2 or 3 live neighbors lives to next generation
                        next.setValue(row, col, (numLiveCells == 2 || numLiveCells == 3));
                    } else {
                        //4. Any dead cell with exactly 3 live neighbors becomes a live cell.
                        next.setValue(row, col, numLiveCells == 3);
                    }
                }
            }
            return next;
        }



        private int checkLive(int row, int col) {
            if ((row < 0) || (col < 0) || (row >= rows) || (col >= cols)) { return 0; }
            return getValue(row, col) ? 1 : 0;
        }

        public int countLiveCells(int row, int col) {
            int number = 0;
            number += checkLive(row - 1,  col - 1);
            number += checkLive(row, col - 1);
            number += checkLive(row + 1, col - 1);
            number += checkLive(row - 1, col);
            number += checkLive(row + 1, col);
            number += checkLive(row - 1, col + 1);
            number += checkLive(row, col + 1);
            number += checkLive(row + 1, col + 1);
            return number;
        }

    }
}



Hope it helps.
Was This Post Helpful? 0
  • +
  • -

#5 kira89  Icon User is offline

  • D.I.C Head

Reputation: 2
  • View blog
  • Posts: 61
  • Joined: 27-November 07

Re: Game of life

Posted 27 September 2009 - 01:31 PM

I'm aware my code is not even remotely well written so there's no need to point it out. I simply don't have time to start over and do it properly now that I've gotten my brain back into programming mode.

Turns out all that was (logically) wrong with my code was that in my checkLive method I had >= and it should just be >. Slight difference between C# and Java of which I wasn't aware. Oh well.

Thanks for your input guys. =)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1