Why is my Conway's Game of Life program not working properly?

1k views Asked by At

I recently came across John Conway's Game of Life and decided to code it in Python. So I did, but it doesn't work as it should.

For example, I have 3 live cells in a row horizontally. If it was working correctly, it would oscillate from 3 live cells in a row to 3 live cells in a column, but it does not do that.

My current code does something like this:

# ("O" == dead cell, "X" == live cell)
# generation 1:
OOO
XXX
OOO

# generation 2:
OOO
OOO
OOO

What it should do is this:

# (O == dead cell, X == live cell)
# generation 1:
OOO
XXX
OOO

# generation 2:
OXO
OXO
OXO

I do not know in which area the code gremlin lives so I just posted the whole code below.

import os
import time

# RULES OF LIFE
#   1.  Any live cell with less than two live neighbours dies, as if caused by under-population.
#   2.  Any live cell with two or three live neighbours lives on to the next generation.
#   3.  Any live cell with more than three live neighbours dies, as if by overcrowding.
#   4.  Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.

def seed():
    board[12][11] = "X"
    board[12][12] = "X"
    board[12][13] = "X"

def display():
    for row in board:
        print("".join(row))

def update():
    for row in range(0, len(board)):
        for column in range(0, len(board[row])):
            neighbors = 0
            for r in range(-1, 2):
                for c in range(-1, 2):
                    if r != 0 and c != 0:
                        try:
                            if board[r][c] == "X":
                                neighbors += 1

                        except IndexError:
                            pass

            if board[row][column] == " ": # dead cell
                if neighbors == 3: # RULE 4.
                    temp_board[row][column] = "X"

            elif board[row][column] == "X": # live cell
                if neighbors < 2: # RULE 1.
                    temp_board[row][column] = " "

                elif neighbors == 2 or neighbors == 3: # RULE 2.
                    pass

                elif neighbors > 3: # RULE 3.
                    temp_board[row][column] = " "

HEIGHT = 25
WIDTH = 25
board = [[" " for column in range(0, WIDTH)] for row in range(0, HEIGHT)]
temp_board = list(board)
seed()
while True:
    display()
    update()
    board = list(temp_board)
    time.sleep(1)
    os.system("cls")

What am I missing?

1

There are 1 answers

2
Austin Hartzheim On BEST ANSWER

I found three mistakes, and there might be more. Here is a summary of what I found. I will explain these in more detail below:

  1. You need to include the offset when checking neighbor cells.
  2. You need to include the surrounding cells when checking the neighbors, not just the cells that are diagonal from the center cell.
  3. You need to do a deepcopy of the board.

Offsets necessary

You start to setup code to handle the offsets here:

for r in range(-1, 2):
    for c in range(-1, 2):

However, you never add the offsets to the original values. Essentially, you are just checking the top few spaces of the grid. So, you need to change if board[r][c] == "X" to if board[row+ r][column + c] == "X".

Include all surrounding cells

You made code to check the surrounding cells (the for loops shown above), and you knew that you needed exempt the cell itself from being tested. You used this code to exclude the cell itself:

if r != 0 and c != 0

However, this condition fails whenever either of the offset variables, r and c, are zero. This happens above, below, to the right, and to the left of the cell. You need to change the condition to this:

if (r != 0) or (c != 0)  # Parenthesis added for readability, not functionality

This way, if either of the offsets are nonzero, you will check the corresponding cell.

Use deepcopy

Assignments like temp_board = list(board) make a new reference to the same data. Essentially, it is the same as assigning an extra name to the same variable. So, when you are updating the board in the update function, you are not updating a temporary copy of the board, but rather the board itself through an alternate name. Then, when subsequent tests are made, the state of the board has changed.

To do a deepcopy in python, you need to add import copy to the file, and then replace your current copy attempts with code like temp_board = copy.deepcopy(board).