dannyxn
dannyxn

Reputation: 422

Python implementation of Conway's Game of Life

For learning purpose I've started creating my implementation of Conway's Game of Life. I've used numpy to store big array, contating dead and alive cells, then I've apllied Conway's rules, to create mechanics of cells life. To manage grid, and graphics I used pygame module. After many reviews, and rewriting code in many ways, I can't find out what's wrong with it, so I decided to ask you. For example I've tried to make a glider, (as code shows), but he dies after 3 loop cycles. I'd be appreciate for help and tips. Can you help me find out why the cells aren't reproducing? Thanks in advance. Code:

import pygame
import numpy as np

BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
N = 195
WIDTH = 10
HEIGHT = 10

grid = np.zeros(shape=(N, N), dtype=np.int32)
glider = np.array([[0, 0, 1],
                       [1, 0, 1],
                       [0, 1, 1]])
grid[3:6, 3:6] = glider

pygame.init()

WINDOW_SIZE = [980, 980]
screen = pygame.display.set_mode(WINDOW_SIZE)

pygame.display.set_caption("GAME OF LIFE")

done = False

clock = pygame.time.Clock()

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    for row in range(N):
        for column in range(N):
            color = BLACK
            if grid[row][column] == 1:
                color = GREEN
            pygame.draw.rect(screen, color,
                             [WIDTH * column,
                              HEIGHT * row,
                              WIDTH,
                              HEIGHT])

    newGrid = grid.copy()
    for i in range(N):
        for j in range(N):
            total = grid[(i-1) % N:(i+1) % N, (j-1) % N:(j+1) % N].sum() - grid[i, j]
            if grid[i, j] == 1:
                if(total < 2) or (total > 3):
                    newGrid[i, j] = 0
                else:
                    if total == 3:
                        newGrid[i, j] = 1
    grid = newGrid

    clock.tick(60)
    pygame.display.flip()

pygame.quit()

Upvotes: 0

Views: 1536

Answers (2)

Tim
Tim

Reputation: 3417

Possibly the else clause in the newGrid loop is over-indented? Because as of now, the grid only goes from 1 to 0 and never from 0 to 1. And it's probably because of the array slicing: [(i-1) % n : (i+1) % n]. There are 2 issues:

  1. this only counts to the index before (i+1) % n, so it doesn't include (i+1) % n.
  2. it doesn't wrap around as you would expect, so e.g. [9:1] doesn't go from 9 to 10 to 0 and stops before 1. It simply returns empty list.

Here's a trick

a.take(range(-1,2),mode='wrap', axis=0).take(range(-1,2),mode='wrap',axis=1)

from this post Wrap slice around edges of a 2D array in numpy. I've modified code below with this trick.

import pygame
import numpy as np

BLACK = (0, 0, 0)
GREEN = (0, 255, 0)
N = 195
WIDTH = 10
HEIGHT = 10

grid = np.zeros(shape=(N, N), dtype=np.int32)
glider = np.array([[0, 0, 1],
                       [1, 0, 1],
                       [0, 1, 1]])
grid[3:6, 3:6] = glider

pygame.init()

WINDOW_SIZE = [980, 980]
screen = pygame.display.set_mode(WINDOW_SIZE)

pygame.display.set_caption("GAME OF LIFE")

done = False

clock = pygame.time.Clock()

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
    for row in range(N):
        for column in range(N):
            color = BLACK
            if grid[row][column] == 1:
                color = GREEN
            pygame.draw.rect(screen, color,
                             [WIDTH * column,
                              HEIGHT * row,
                              WIDTH,
                              HEIGHT])

    newGrid = grid.copy()
    for i in range(N):
        for j in range(N):
            total = grid.take(range(i-1,i+2),mode='wrap', axis=0).take(range(j-1,j+2),mode='wrap',axis=1).sum() - grid[i, j]
            if grid[i, j] == 1:
                if(total < 2) or (total > 3):
                    newGrid[i, j] = 0
            else:
                if total == 3:
                    newGrid[i, j] = 1
    grid = newGrid

    clock.tick(60)
    pygame.display.flip()

pygame.quit()

Upvotes: 0

Davide Fiocco
Davide Fiocco

Reputation: 5914

I think you have a couple of subtle bugs in the way you implement Conway's rules. See my comments in the code for details:

for i in range(N):
    for j in range(N):
        # I changed the range of the extent of the sum, note +2 rather than +1 !
        total = grid[(i-1) % N:(i+2) % N, (j-1) % N:(j+2) % N].sum() - grid[i, j]
        if grid[i, j] == 1:
            if(total < 2) or (total > 3):
                newGrid[i, j] = 0
            # allow for survival in case of total = 2 or 3 (this could be dropped though)
            else:
                newGrid[i, j] = 1
        # allow for reproduction if cell is empty
        else:
            if total == 3:
                newGrid[i, j] = 1

With these edits the glider should glide :)

Upvotes: 1

Related Questions