Search code examples
pythonarraysconways-game-of-life

Python Conway's Game of Life not behaving properly for glider gun design


I'm trying to replicate Conway's Game of Life in Python. The rules of this simulation and a properly working version of it can be found here: https://bitstorm.org/gameoflife/

In my version, when I randomly assign cells to being alive at the start, it appears to behave properly, with the classic amourphous blobs of cells expanding over the screen.

However, when I replicate the "glider gun" arrangement (which can also be seen on the linked website), the cells are not updating properly: the structures decay slightly and then the movement of the cells remains stagnant.

This leads me to believe I have a logical error in my code - any help would be appreciated!

This is the section of my code within my Cell class that updates its survival based on its neighbors (the eight cells surrounding it):

def update(self, neighbors):
    numAliveNeighbors = 0
    for neighbor in neighbors:
        if neighbor.isAlive:
            numAliveNeighbors+=1

    if numAliveNeighbors <= 1 or numAliveNeighbors >=4:
        self.isAlive = False
    elif not self.isAlive and numAliveNeighbors is 3:
        self.isAlive = True

This is the section of my code that finds all of the neighbors of every cell and calls the update method on them:

for row in range(len(cells)):
    for column in range(len(cells[row])):
            neighbors = []
            for tempRow in range (row-1, row + 2):
                for tempColumn in range (column-1, column + 2):
                    if tempRow >= 0 and tempRow < len(cells):
                        if tempColumn >= 0 and tempColumn < len(cells[tempRow]):
                            if not(tempRow is row and tempColumn is column):
                                neighbors.append(cells[tempRow][tempColumn])
            cells[row][column].update(neighbors)

Solution

  • In-Place Updates

    You're doing in-place updates; that is, you update each cell individually. What you need to do is create a cloned grid and each time you update a cell on an iteration, update the cloned grid, then set the game board to the cloned grid. Otherwise, cells are going to be updated based on the current iteration, which doesn't make sense.

    What you could do is something like this:

    def isAlive(alive, neighbours):
        return (alive and 2 <= neighbours <= 3) or (not alive and neighbours == 3)
    
    def update(cells):
        grid = [[0] * len(row) for row in cells]
        for row in range(len(cells)):
            for col in range(len(cells[row])):
                neighbours = 0
                for tr in range(row - 1, row + 2):
                    for tc in range(col - 1, col + 2):
                        if (tr != row or tr != col) and cells[tr][tc]:
                            neighbours += 1
                grid[row][col] = isAlive(cells[row][col], neighbours)
    return grid
    

    Then you can call cells = update(cells) in a loop.