Search code examples
pythonpython-3.xlistconways-game-of-life

Problem trying to implement 'Game of Life' in python as a beginner


So I am an absolute beginner in python and tried my hand at implementing Conway's game of life. I am not using any libraries at all, my grid is just a 50x50 matrix of 1s and 0s. The next_gen output I get is not matching the expected output but I couldn't figure out why, any help would be appreciated.

Here's my code:

def alive_neighbours(grid, r, c):
    count = 0
    if grid[r-1][c-1] == 1:
        count += 1
    if grid[r-1][c] == 1:
        count += 1
    if grid[r-1][c+1] == 1:
        count += 1
    if grid[r][c-1] == 1:
        count += 1
    if grid[r][c+1] == 1:
        count += 1
    if grid[r+1][c-1] == 1:
        count += 1
    if grid[r+1][c] == 1:
        count += 1
    if grid[r+1][c+1] == 1:
        count += 1
    return count


grid = [[0 for i in range(50)] for j in range(50)]
grid[25][25] = 1
grid[26][26] = 1
grid[27][24] = 1
grid[27][25] = 1
grid[27][26] = 1
grid[49][49] = 1


def next_gen(grid):
    new_grid = grid[:]
    for r in range(1, 49):
        for c in range(1, 49):
            neighbour = alive_neighbours(grid, r, c)
            if (r == 0 or c == 0) or (r == 49 or c == 49):
                pass                                # I am yet to define edge case
            else:
                if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
                    new_grid[r][c] = 0
                    continue
                elif grid[r][c] == 1 and (neighbour == 2 or 3):
                    continue
                elif grid[r][c] == 0 and neighbour == 3:
                    new_grid[r][c] = 1
                    continue
                else:
                    continue
    grid = new_grid[:]


def printf(grid):
    for r in range(50):
        for c in range(50):
            if grid[r][c] == 1:
                print("*", end=" ")
            else:
                print(" ", end=" ")
        print("")


x = 0
while x != '-1':
    x = (input("x: "))
    printf(grid)
    next_gen(grid)

I also tried rewriting my next_gen function, but using that there is absolutely no change in the matrix

next_gen:

def next_gen(grid):
    new_grid = grid[:]
    for r in range(1, 49):
        for c in range(1, 49):
            neighbour = alive_neighbours(grid, r, c)
            if (r == 0 or c == 0) or (r == 49 or c == 49):
                pass
            else:
                if grid[r][c] == 1 and neighbour == 2 or 3:
                    continue
                if grid[r][c] == 0 and neighbour == 3:
                    new_grid[r][c] = 1
                    continue
                if grid[r][c] == 1:
                    new_grid[r][c] = 0
                continue
    grid = new_grid[:]

Solution

  • As bruno said in his answer there are a few issues in your code, He already told about your issue with grid and how allocating to it in the function actually points the local scope version to the new grid and not the global scope one. He also covers how to resolve this.

    The other issue you will have is that you have undertood that just doing new_grid = grid will mean that new_grid and grid point at the same list. So to prevent this you have correctly done new_grid = grid[:] as this will create a new list in memory and copy the data from the grid list. However thats a shallow copy, so you will create a new list object but copy all the list references inside your list. we can demonstrate this by doing a shallow copy of a list and then changing a value in the new list.

    
    grid_size = 2
    grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
    new_grid = grid[:]
    new_grid[1][1] = "R"
    print("grid:", grid)
    print("newg:", new_grid)
    
    #output#
    grid: [[0, 0], [0, 'R']]
    newg: [[0, 0], [0, 'R']]
    

    So you can see that changing the inner list in one will change the inner list in the other. so you need to do a deep copy of the list so that your not changing the original grid as you go. Since conways states are based on the original grid state and squares changing shouldnt impact the others. I think your already aware of this concept.

    I also made a change to the alive neighbours to simplyfy it. Below is a quick draft adaptation. when you run it you should see your glider heading off to the bottom right corner

    from copy import deepcopy
    
    
    def alive_neighbours(grid, r, c):
        differences = (0, -1, +1)
        cells_in_square = [(r + a, c + b) for a in differences for b in differences]
        total = 0
        for x,y in cells_in_square[1:]:
            try:
                if x >=0 and y>=0:
                    total += grid[x][y]
            except IndexError as ie:
                pass #ignore index errors as at the edge of the grid
        return total
    
    
    def next_gen(grid):
        new_grid = deepcopy(grid)
        for r in range(len(grid)):
            for c in range(len(grid)):
                neighbour = alive_neighbours(grid, r, c)
                if grid[r][c] == 1 and (neighbour > 3 or neighbour < 2):
                    new_grid[r][c] = 0
                elif grid[r][c] == 0 and neighbour == 3:
                    new_grid[r][c] = 1
        return new_grid
    
    
    def printf(grid):
        for r in grid:
            for c in r:
                if c == 1:
                    print("*", end=" ")
                else:
                    print(" ", end=" ")
            print("")
    
    
    grid_size = 50
    grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
    grid[25][25] = 1
    grid[26][26] = 1
    grid[27][24] = 1
    grid[27][25] = 1
    grid[27][26] = 1
    grid[49][49] = 1
    
    while True:
        x = (input("press enter to see next grid: "))
        if x:
            break
        printf(grid)
        grid = next_gen(grid)
    

    UPDATE

    other then the glider you started with the below is a nice start for a cool exploder

    grid_size = 50
    grid = [[0 for i in range(grid_size)] for j in range(grid_size)]
    grid[25][25] = 1
    grid[26][24] = 1
    grid[26][25] = 1
    grid[26][26] = 1
    grid[27][24] = 1
    grid[27][26] = 1
    grid[28][25] = 1