Search code examples
pythonconways-game-of-life

(Closed) Conway's Game of Life not updating correctly (Python)


This is a snippet of code from Conway's Game of Life simulation made with Python. It's pretty simplistic for the purpose of checking whether it works - and it doesn't. Cannot figure out why, but as far as I can tell, it's something with the updating.

Would appreciate any input as to why it updates the way it does:

Gif in pygame (same code): https://i.sstatic.net/Upuc5.jpg Not updating correctly: https://i.sstatic.net/bb6tV.jpg

import pprint,random
#here we make the initial board, 6x6
board = []
for y in range (6):
    row = []
    for x in range (6):
        row.append(random.randint(0,1))
    board.append(row)
#and display it
pprint.pprint(board)

#this function counts the neighbours for each cell
def neighbours(x,y):
    counter = 0
    neighbours = \
    [(x-1,y+1),(x,y+1),(x+1,y+1),\
    (x-1,y),          (x+1,y),\
    (x-1,y-1),(x,y-1),(x+1,y-1)]
    for n in neighbours:
        a, b = n
        try:
            counter += board[a][b]
        except:
            pass
#printed out the counter to check if it counted correctly (it does, as far as I could tell)
    if x == 4 and y == 4:
        print(counter)
    return counter

#function to make a new board based off the old one - basically, the updater. 
#here's where the problem might lie - but for the life of me I cannot tell where and why.
def new(board):
    new_board = []
    for y in range(6):
        new_row = []
        for x in range(6):
            n = neighbours(x,y)
            oldcell = board[x][y]
            #everything is set up to be according to the rules
            #(if dead(0) can only come alive with 3 alive cells
            #(if alive(1) can continue to live with exactly 2 or 3 live neighbours
            if oldcell == 0:
                newcell = 0
                if n == 3:
                    newcell = 1
            elif oldcell == 1:
                newcell = 1
                if n > 3 or n < 2:
                    newcell = 0
            new_row.append(newcell)
        new_board.append(new_row)
    return new_board

#displaying the board for 6 instances
for i in range (6):
    nboard = new(board)
    board = nboard
    pprint.pprint(board)

Thanks in advance!


Solution

  • You are mixing up rows and columns in your loops, effectively transposing the entire matrix with each new board. The loops should look like this:

    for x in range(6):
        new_row = []
        for y in range(6):
            ...
    

    Also, the bulk of your if/else logic in the loop could be simplified to a single line. Whether that's easier to read and understand or not is for you to decide.

    def new(board):
        new_board = []
        for x in range(6):
            new_row = []
            for y in range(6):
                n = neighbours(x,y)
                oldcell = board[x][y]
                newcell = int(n in (2, 3)) if oldcell else int(n == 3)
                new_row.append(newcell)
            new_board.append(new_row)
        return new_board
    

    As noted in comments, there's a second (though less obvious) problem: While your try/except to skip all the off-board indices was a neat idea, it does not catch e.g. x-1 for x = 0, as [-1] is a legal index in Python (indexing the last element in the sequence), making your board "wrap around" two of the four edges. Instead, you should explicitly check for the bounds of the board. If you want, you could make this another 'fun' one-liner:

    def neighbours(x,y):
        return sum(board[a][b] for a in (x-1,x,x+1) for b in (y-1,y,y+1) 
                   if (a,b) != (x,y) and 0 <= a < len(board) and 0 <= b < len(board[a]))
    

    Of course, you can also keep your loop and just replace the try/except for the if check.