I have a confession to make. Although I have a degree in Computer Science, I don’t ever recall implementing John Conway’s Game of Life. The program is less of a game and more about implementing an algorithm with observable results. In this post, we’ll implement Conway’s Game of Life using .NET, C#, and some fun emojis.

What Are The Rules

In 1970, British mathematician John Conway developed a zero-player game designed to mimic the behavior of life itself. Players are required to see the universe with an initial state, and then observe as the rules of life produce an outcome. Each game consists of a two-dimensional grid with a potential state of life or death enhabiting each cell. Each cell has the potential to shift its state based on a set of rules.

What are the rules to play the game of life?

  1. Any live cell with fewer than two live neighbors dies, as if by underpopulation.
  2. Any live cell with two or three live neighbors lives on to the next generation.
  3. Any live cell with more than three live neighbors dies, as if by overpopulation.
  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

For programming, we can reduce these rules to a set of three algorithm steps.

  1. Any live cell with two or three live neighbors survives.
  2. Any dead cell with three live neighbors becomes a live cell.
  3. All other live cells die in the next generation. Similarly, all other dead cells stay dead.

Each generation continues to seed the next generation until the universe reaches an equilibrium. The game is a fun one to watch and hypnotic based on the size of the board.

reference: Wikipedia: Conway’s Game of Life

Game of Life in C#

We’ll be using a C# console application to implement the rules to the Game of Life. For the sake of following along, we’ll paste the app in its entirety below.

using System;
using System.Security.Cryptography;
using System.Text;
using System.Threading;

namespace GameOfLife
{
    static class Program
    {
        const int Rows = 25;
        const int Columns = 50;

        static bool runSimulation = true;

        public static void Main()
        {
            var grid = new Status[Rows, Columns];

            // randomly initialize our grid
            for (var row = 0; row < Rows; row++)
            {
                for (var column = 0; column < Columns; column++)
                {
                    grid[row, column] = (Status) RandomNumberGenerator.GetInt32(0, 2);
                }
            }

            Console.CancelKeyPress += (sender, args) =>
            {
                runSimulation = false;
                Console.WriteLine("\n👋 Ending simulation.");
            };
            
            // let's give our console
            // a good scrubbing
            Console.Clear();

            // Displaying the grid 
            while (runSimulation)
            {
                Print(grid);
                grid = NextGeneration(grid);
            }
        }

        private static Status[,] NextGeneration(Status[,] currentGrid)
        {
            var nextGeneration = new Status[Rows, Columns];

            // Loop through every cell 
            for (var row = 1; row < Rows - 1; row++)
            for (var column = 1; column < Columns - 1; column++)
            {
                // find your alive neighbors
                var aliveNeighbors = 0;
                for (var i = -1; i <= 1; i++)
                {
                    for (var j = -1; j <= 1; j++)
                    {
                        aliveNeighbors += currentGrid[row + i, column + j] == Status.Alive ? 1 : 0;
                    }
                }

                var currentCell = currentGrid[row, column];
                
                // The cell needs to be subtracted 
                // from its neighbors as it was  
                // counted before 
                aliveNeighbors -= currentCell == Status.Alive ? 1 : 0;
  
                // Implementing the Rules of Life 
  
                // Cell is lonely and dies 
                if (currentCell == Status.Alive && aliveNeighbors < 2)
                {
                    nextGeneration[row,column] = Status.Dead;
                }

                // Cell dies due to over population 
                else if (currentCell == Status.Alive && aliveNeighbors > 3)
                {
                    nextGeneration[row, column] = Status.Dead;
                }

                // A new cell is born 
                else if (currentCell == Status.Dead && aliveNeighbors == 3)
                {
                    nextGeneration[row,column] = Status.Alive;
                }
                // stays the same
                else
                {
                    nextGeneration[row, column] = currentCell;
                }
            }
            return nextGeneration;
        }

        private static void Print(Status[,] future, int timeout = 500)
        {
            var stringBuilder = new StringBuilder();
            for (var row = 0; row < Rows; row++)
            {
                for (var column = 0; column < Columns; column++)
                {
                    var cell = future[row, column];
                    stringBuilder.Append(cell == Status.Alive ? "😁" : "💀");
                }
                stringBuilder.Append("\n");
            }

            Console.BackgroundColor = ConsoleColor.Black;
            Console.CursorVisible = false;
            Console.SetCursorPosition(0, 0);
            Console.Write(stringBuilder.ToString());
            Thread.Sleep(timeout);
        }
    }
    
    public enum Status
    {
        Dead,
        Alive,
    }
}

Some of the cool things to note in the code above include:

  1. A variable row and column setup.
  2. The use of RandomNumberGenerator to seed our grid.
  3. Listening for CancelKeyPress event to stop the simulation.
  4. The Print method builds a single string then writes to the console by repositioning the cursor.
  5. The logic and UI are separated, allowing us to move the logic to another display format.

When we run the program, we see the following.

dead/alive

We can play with the emojis and get different outputs. Here is an example of aliens and astronauts.

astronaut/alien

Here is an example of cats and mice.

cat/mouse

Here is a zombie apocalypse inspired layout.

zombie/alien

With emojis and the game of life, the possibilities are endless. What other combinations are possible? Leave them in the comments below. The code is available on GitHub.