The Game Of Life is a cell life simulator devised by the Cambridge mathematician John Conway.
The simulator serves as an excellent coding example to learn new programming languages. The rules are simple. You have a grid in a predetermined size, each cell can live or die based on a set of rules:
- If a cell is alive, and there are 0 or 1 living neighbors, the cell dies of loneliness.
- If a cell is alive, and there are 2 or 3 living neighbors, the cell survives.
- If a cell is alive, and there are 4 or more living neighbors, the cell dies of overpopulation.
- If a cell is dead, and there are 3 living neighbors, the cell comes alive.
This is an example of a simple game of life implementation in C# and .net 8. The simulation uses the console output to visualize the game progress.
UPDATE: Code have been updated to recalculate the complete board before progressing. Thanks to Alexander Gats for that one!
STEP 1: DEFINE THE GRID SIZE AND PRE-SEED A NUMBER OF CELLS
const int BOARD_WIDTH = 80;
const int BOARD_LENGTH = 25;
const int ACTIVE_CELLS_AT_START = 400;
bool[,] board = new bool[BOARD_WIDTH, BOARD_LENGTH];
var random = new Random();
// Populate a number of cells to begin with
for (int i = 0; i < ACTIVE_CELLS_AT_START; i++)
{
board[random.Next(board.GetLength(0)), random.Next(board.GetLength(1))] = true;
}
First we define the game prerequisites; the size of the grid (called board) and how many cells should be alive when the simulation starts.
Then we populate the grid by randomly setting cells in the grid to true (true meaning alive, false meaning dead).
STEP 2: MAKE AN ALGORITHM TO CALCULATE THE NUMBER OF LIVING NEIGHBORS
int CountNeighbors(bool[,] board, int x, int y)
{
int ac = 0;
// Double loop from 1 up and 1 left to 1 down and 1 right
// Gives 9 iterations
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
// Only count cells within the board (IsValidIndex)
// and do not count the cell itself (IsYourself)
if (IsValidIndex(i, j, board.GetLength(0), board.GetLength(1))
&& !IsYourself(x, y, i, j)
&& board[i, j])
ac++;
}
}
return ac;
}
bool IsValidIndex(int i, int j, int rows, int cols)
{
return i >= 0 && i < rows && j >= 0 && j < cols;
}
bool IsYourself(int x, int y, int i, int j)
{
return x == i && y == j;
}
The CountNeighBors takes the game grid and the x/y position of the cell to calculate how many of the 8 neighboring cells are alive.
The algorithm needs a double loop to check the neighbors.
But it also needs to be smart enough to count even if the cell is on the edge of the grid. The IsValidIndex checks to see if the cell being investigated is within the board.
Also, we should not count the cell being investigated. The IsYourself checks to see if the cell being investigated is the current cell, disregard that cell.
STEP 3: CALCULATE THE CELL LIFE
bool CalculateCellLife(bool[,] board, int x, int y)
{
int neighbors = CountNeighbors(board, x, y);
// If cell is alive:
if (board[x, y] == true)
{
// 0 or 1 neighbors: Die of loneliness
if (neighbors <= 1)
return false;
// 4 or more neighbors: Die of overpopulation
if (neighbors >= 4)
return false;
return true;
}
// If cell is not alive:
else
{
// 3 neighbors give birth to a new cell
if (neighbors == 3)
return true;
return false;
}
}
This is the actual game of life algorithm.
The method uses the CountNeighbors function to calculate the number of alive cells surrounding a specified cell, and return true if cell is alive and false if cell is dead.
As stated before, the rules are that a living cell will die if there are 0,1 or more than 4 alive surrounding cells, and come alive if there is precisely 3 living cells surrounding it.
STEP 4: MAKE A FUNCTION TO PROGRESS THE SIMULATION BY ONE STEP
bool[,] StepSimulation(bool[,] currentBoard)
{
// Recalculate a new board based on the values
// of the current board
bool[,] newBoard = new bool[BOARD_WIDTH, BOARD_LENGTH];
for (int x = 0; x < board.GetLength(0); x++)
{
for (int y = 0; y < board.GetLength(1); y++)
{
newBoard[x,y] = CalculateCellLife(currentBoard, x, y);
}
}
return newBoard;
}
Here we calculate the next step in the simulation by creating a new board based on the values of the old board.
STEP 5: DRAW A CELL IN THE CONSOLE
void DrawCell(int x, int y, bool isAlive)
{
Console.SetCursorPosition(x, y);
if (!isAlive)
Console.BackgroundColor = ConsoleColor.Black;
else
Console.BackgroundColor = ConsoleColor.White;
Console.Write(" ");
}
To draw a cell in the console I simply move the cursor to the determined position and draws a space with either black (dead) or white (alive) background.
STEP 6: THE MAIN GAME LOOP
while (true)
{
// Progress the simulation
board = StepSimulation(board);
// Draw the new board
for (int x = 0; x < board.GetLength(0); x++)
{
for (int y = 0; y < board.GetLength(1); y++)
{
DrawCell(x, y, board[x, y]);
}
}
}
This is the main game loop that loops forever, takes each cell on the board, calculates if the cell must live or die, and draws the result to screen.
COMPLETE CODE:
This is the code in its completion:
const int BOARD_WIDTH = 80;
const int BOARD_LENGTH = 25;
const int ACTIVE_CELLS_AT_START = 400;
bool[,] board = new bool[BOARD_WIDTH, BOARD_LENGTH];
var random = new Random();
// Populate a number of cells to begin with
for (int i = 0; i < ACTIVE_CELLS_AT_START; i++)
{
board[random.Next(board.GetLength(0)), random.Next(board.GetLength(1))] = true;
}
while (true)
{
// Progress the simulation
board = StepSimulation(board);
// Draw the new board
for (int x = 0; x < board.GetLength(0); x++)
{
for (int y = 0; y < board.GetLength(1); y++)
{
DrawCell(x, y, board[x, y]);
}
}
}
bool[,] StepSimulation(bool[,] currentBoard)
{
// Recalculate a new board based on the values
// of the current board
bool[,] newBoard = new bool[BOARD_WIDTH, BOARD_LENGTH];
for (int x = 0; x < board.GetLength(0); x++)
{
for (int y = 0; y < board.GetLength(1); y++)
{
newBoard[x,y] = CalculateCellLife(currentBoard, x, y);
}
}
return newBoard;
}
bool CalculateCellLife(bool[,] board, int x, int y)
{
int neighbors = CountNeighbors(board, x, y);
// If cell is alive:
if (board[x, y] == true)
{
// 0 or 1 neighbors: Die of loneliness
if (neighbors <= 1)
return false;
// 4 or more neighbors: Die of overpopulation
if (neighbors >= 4)
return false;
return true;
}
// If cell is not alive:
else
{
// 3 neighbors give birth to a new cell
if (neighbors == 3)
return true;
return false;
}
}
int CountNeighbors(bool[,] board, int x, int y)
{
int ac = 0;
// Double loop from 1 up and 1 left to 1 down and 1 right
// Gives 9 iterations
for (int i = x - 1; i <= x + 1; i++)
{
for (int j = y - 1; j <= y + 1; j++)
{
// Only count cells within the board (IsValidIndex)
// and do not count the cell itself (IsYourself)
if (IsValidIndex(i, j, board.GetLength(0), board.GetLength(1))
&& !IsYourself(x, y, i, j)
&& board[i, j])
ac++;
}
}
return ac;
}
bool IsValidIndex(int i, int j, int rows, int cols)
{
return i >= 0 && i < rows && j >= 0 && j < cols;
}
bool IsYourself(int x, int y, int i, int j)
{
return x == i && y == j;
}
void DrawCell(int x, int y, bool isAlive)
{
Console.SetCursorPosition(x, y);
if (!isAlive)
Console.BackgroundColor = ConsoleColor.Black;
else
Console.BackgroundColor = ConsoleColor.White;
Console.Write(" ");
}
MORE TO READ:
- Game Of Life from Wikipedia
- John Conway from Wikipedia