Question

I'm trying to make conway's game of life in python using pygame. I can't figure out why but the patterns its generating are way off. I've looked through the code a million times and I can't see whats wrong.

Here is a screenshot of one of the generations gameoflife

And here is the complete code of the game.

import pygame, sys
from pygame.locals import *
from random import randint
import numpy

#inititalize
pygame.init()
clock = pygame.time.Clock()

#constants
FPS = 10
BLACK = (0,0,0)
RED = (255,0,0)
GREY = (30,30,30)
SCREENX = 640
SCREENY = 480
CELLSIZE = 10
HEIGHT = SCREENY/CELLSIZE
WIDTH = SCREENX/CELLSIZE

#set up window
window = pygame.display.set_mode((SCREENX, SCREENY))
pygame.display.set_caption('Game of Life')
window.fill(BLACK)

#generate random seed
cells = numpy.zeros((WIDTH,HEIGHT), dtype=numpy.int)
for x in range(0,WIDTH):
    for y in range(0,HEIGHT):
        #0 is a dead cell, 1 is an alive cell
        cells[x][y] = randint(0,1)

def findNeighbors(grid, x, y):
    if 0 < x < len(grid) - 1:
        xi = (0, -1, 1)
    elif x > 0:
        xi = (0, -1)
    else:
        xi = (0, 1)

    if 0 < y < len(grid[0]) - 1:
        yi = (0, -1, 1)
    elif y > 0:
        yi = (0, -1)
    else:
        yi = (0, 1)

    for a in xi:
        for b in yi:
            if a == b == 0:
                continue
            yield grid[x + a][y + b]

def update(grid, x, y):
    #determine num of living neighbors
    neighbors = findNeighbors(cells,x,y)
    alive = 0
    for i in neighbors:
        if i == 1:
            alive+=1

    #if current cell is alive
    if grid[x][y] == 1:
        #kill if less than 2 or more than 3 alive neighbors
        if (alive < 2) or (alive > 3):
            return 0
        else:
            return 1
    #if current cell is dead
    elif grid[x][y] == 0:
        #make alive if 3 alive neighbors
        if alive == 3:
            return 1
        else:
            return 0

#main loop
while True:

    #check if user wants to exit
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

    #update cells
    for x in range(0,WIDTH):
        for y in range(0,HEIGHT):
            cells[x][y] = update(cells,x,y)

    #draw grid
    for x in range(0,SCREENX,CELLSIZE):
        for y in range(0,SCREENY,CELLSIZE):
            #if cell is alive
            if cells[x/CELLSIZE][y/CELLSIZE] == 1:
                #draw red square
                pygame.draw.rect(window, RED, [x, y, CELLSIZE, CELLSIZE])
            else:
                #draw black square
                pygame.draw.rect(window, BLACK, [x, y, CELLSIZE, CELLSIZE])
            #draw square border
            pygame.draw.rect(window, GREY, [x, y, CELLSIZE, CELLSIZE], 1)

    #draw updates
    pygame.display.update()

    #generations per second
    clock.tick(FPS)

I don't think the problem is with the findNeighbors function since I got it from this stackoverflow answer. So I assume the problem is in the update function but I can't see where the logic is wrong based on the game rules.

Was it helpful?

Solution

I suspect this is the problem:

cells[x][y] = update(cells,x,y)

You've only got one grid, which you're updating while you're still computing from it. Generation n+1 should only take account of information from generation n - whereas currently you've got a mixture of information from n and n+1. You'll end up with the new values of the neighbours above and to the left of you, and the old values of the neighbours to the right and below you, because they haven't been recomputed yet.

So for example, take this pattern (where # means "alive"):

...
###
...

That should go to:

.#.
.#.
.#.

... but in fact, you'll end up with (I think):

.##
#.#
... // Bottom-middle only has one live neighbour at computation time

By the time we compute top right, it has three neighbours. The left-middle has 2 live neighbours at computation time; the centre has 4, and the right-middle has 2. Nothing on the bottom row has three neighbours by the time it's computed, so it stays dead.

Typically a Conway's Life implementation will either compute an entirely new grid for each generation, or it will flip between two grids, computing all of one from the other.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top