Conway's Game of Life: unexpected behavior

49 Views Asked by At

I'm currently try to recreate Conway's Game of Life in a .NET console application.

I hardcoded this pattern

1

which does basically not move and just stays that way through generations; now though I came to the problem that after one generation everything dies.

using System.Linq.Expressions;
using System.Security.Cryptography.X509Certificates;

internal class Program
{
    private static Cell[,] cells;
    private const int MAX = 6;

    public static void Main()
    {
        var rand = new Random();
        cells = new Cell[MAX, MAX];

        for (int row = 0; row < cells.GetLength(0); row++)
        {
            for (int column = 0; column < cells.GetLength(1); column++)
            {
                //  cells[row,column] = new Cell((rand.Next(0,3) == 1) ? '#' : '0');
                cells[row, column] = new Cell('0');
            }
        }

        cells[1, 0] = new Cell('#');
        cells[0, 1] = new Cell('#');
        cells[2, 1] = new Cell('#');
        cells[1, 2] = new Cell('#');

        updateConsole();
        Thread.Sleep(1000);

        while (true)
        {
            Thread.Sleep(1000);

            nextGeneration();
        }
    }

    static void updateConsole()
    {
        Console.Clear();

        for (int y = 0; y < cells.GetLength(1); y++)
        {
            for (int x = 0; x < cells.GetLength(0); x++)
            {
                if (cells[x, y].state == '#') 
                    Console.ForegroundColor = ConsoleColor.DarkYellow;
                else 
                    Console.ForegroundColor = ConsoleColor.DarkBlue;

                Console.Write(cells[x, y].state + " ");
            }

            Console.Write("\n");
        }
    }

    static void nextGeneration()
    {
        for (int _x = 0; _x < MAX; _x++) 
            for (int _y = 0; _y < MAX; _y++) 
                cells[_x, _y].lastState = cells[_x, _y].state;

        for (int y = 0; y < cells.GetLength(1); y++)
        {
            for (int x = 0; x < cells.GetLength(0); x++)
            {
                //cells[x, y].lastState = cells[x, y].state;
                int alive = 0;
                int dx = 0, dy = 0;

                for (int i = 0; i < 6; i++)
                {
                    dx = (int)Math.Round(Math.Cos(i * 45)) + x;
                    dy = (int)Math.Round(Math.Sin(i * 45)) + y;

                    if ((dx >= 0) && (dx < MAX) && (dy >= 0) && (dy < MAX)) 
                    { 
                         if (cells[dx, dy].lastState == '#') 
                             alive++; 
                    }
                }

                if (cells[x, y].state == '0')
                {
                    if (alive == 3) 
                        cells[x, y].state = '#';
                }

                if (cells[x, y].lastState == '#')
                {
                    if (alive == 2 || alive == 3) 
                    {
                        cells[x, y].state = '#'; 
                    }
                    else 
                    {
                        cells[x, y].state = '0'; 
                    }
                }

                //Console.WriteLine("x:" +x +" y:" +y + " = alive:" + alive);
            }

            updateConsole();
        }
    }

    class Cell
    {
        public Cell(char _state)
        {
            this.state = _state;
            this.lastState = _state;
        }

        public char state { get; set; }
        public char lastState { get; set; }
    }
}

// 0 = Dead
// # = alive

I tried to make sure the directions weren't unnecessarily counted twice and checked via debugging if it counted the living neighbours right; So far, no problem. After that I looked if I implemented the game rules right; seemed like it.

1

There are 1 best solutions below

0
Luke Woodward On BEST ANSWER

There are two things that are incorrect about your code. Both of them are in this section:

                for (int i = 0; i < 6; i++)
                {
                    dx = (int)Math.Round(Math.Cos(i * 45)) + x;
                    dy = (int)Math.Round(Math.Sin(i * 45)) + y;

                    if ((dx >= 0) && (dx < MAX) && (dy >= 0) && (dy < MAX)) { if (cells[dx, dy].lastState == '#') alive++; }
                }

The first problem is that there are eight neighbouring squares you need to check, not six. So the 6 in the for loop needs to be 8.

Secondly, Math.Cos and Math.Sin both work in radians rather than degrees. 45° is the same angle as π/4 radians. Replace the use of 45 in your call to both Math.Cos and Math.Sin with Math.PI / 4.

I made these changes to your code, and it worked as expected.


While the above changes work, it seems a little odd to me to use trigonometry for something like this. A simpler solution is to have dx run in a loop from x - 1 to x + 1, and similarly for dy with y, and skip the case where dx == x and dy == y:

                for (dy = y - 1 ; dy <= y + 1; dy++)
                {
                    for (dx = x - 1; dx <= x + 1; dx++)
                    {
                        if (dx != x || dy != y)
                        {
                            if ((dx >= 0) && (dx < MAX) && (dy >= 0) && (dy < MAX)) { if (cells[dx, dy].lastState == '#') alive++; }
                        }
                    }
                }