I tried to make Game of Life by John Conway in pygame. Not sure exactly what went wrong

Question:

The cells don’t behave like they’re supposed to, meaning they don’t follow the rules. I’ve tried everything I could think of, but it doesn’t work.

The variable "state" is used to store the current states of all the cells (1 if active 0 if not) in the format state[x][y], similarly, "cells" also follow the same format but instead of 1s and 0s, it stores all the sprites, aka cells. The "new_state" is exactly like the state but it stores the states for the next generation. (The next generation is calculated from "state" and stored in "new_state".)

Heres an example of what "state" might look like:

state = [[0,0,0,0,0,0,0,0,0],[0,1,1,1,0,0,0], . . . ]

To calculate how many active neighbors there are next to a cell, I just did a nested for loop, both with range(-1, 2). If i, j are the variables of the for loops, then that would look something like this:

for i in range(-1, 2):
    for j in range(-1, 2):
        sum += state[x + i][y + j]
sum -= state[x][y]

Anyways, heres the code:

import pygame as pg
pg.init()
ticking = False

# colours
white = (255, 255, 255)
grey = (100, 100, 100)
blue = (0, 0, 255)
black = (0, 0, 0)
drk_blue = (0, 0, 100)
red = (255, 0, 0)
# now the screen
width = 750
height = 500

main_screen = pg.display.set_mode([width, height])
main_screen_rect = main_screen.get_rect()
game_width = width - width // 5
main_screen.fill(black)
game_screen = pg.Surface([game_width, height])
game_screen_rect = game_screen.get_rect()
game_screen.fill(black)

WH_cells = [0, 42]
for x in range(0, game_width, 12):
    WH_cells[0] += 1
a = False  # This is for toggling the eraser for the cells

# The state says which cells are active and inactive and cells is just a list containing the sprites
state = []
cells = []
new_state = []
# New state is for updating, i.e., it contains the states of the next generation

# imp functions


def logic():
    sum_calc()
    drawer()
    global state, new_state
    state = new_state
    new_state = blank


def sum_calc():
    global new_state
    state_len = len(state)
    state_len_part = len(state[0])
    for x_c in range(1, state_len - 1):
        for y_c in range(1, state_len_part - 1):

            neigh_sum = 0
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if x_c + i < state_len and y_c + j < len(state[x_c + i]):
                        neigh_sum += state[x_c + i][y_c + j]
            neigh_sum -= state[x_c][y_c]

            if neigh_sum < 2 or neigh_sum > 3:
                new_state[x_c][y_c] = 0
            elif neigh_sum == 3:
                new_state[x_c][y_c] = 1


def drawer():
    state_len = len(new_state)
    state_len_part = len(new_state[0])

    for x in range(state_len):
        for y in range(state_len_part):

            if new_state[x][y] != 1:
                cells[x][y].Activate(False)
            else:
                cells[x][y].Activate(True)


# sprites


class Cell(pg.sprite.Sprite):

    def __init__(self):
        super(Cell, self).__init__()
        self.surf = pg.Surface((10, 10))
        self.surf.fill(grey)
        self.rect = self.surf.get_rect()
        self.index = None

    def update(self, mouse_pos, eraser):
        if self.rect.collidepoint(mouse_pos[0], mouse_pos[1]):
            cell_x = self.index[0]
            cell_y = self.index[1]
            global state

            if not eraser:
                self.surf.fill(white)
                state[cell_x][cell_y] = 1

            else:  # if eraser
                self.surf.fill(grey)
                state[cell_x][cell_y] = 0

    def Activate(self, yesno):
        global new_state
        cell_x = self.index[0]
        cell_y = self.index[1]
        if yesno:
            self.surf.fill(white)
            new_state[cell_x][cell_y] = 1
        else:
            self.surf.fill(grey)
            new_state[cell_x][cell_y] = 0


all_sprites = pg.sprite.Group()
running = True

# generating the cells and lists
for x in range(0, game_width, 12):
    state.append([])
    cells.append([])
    for y in range(0, height, 12):
        x_coord = int(x / 12)
        state[x_coord].append(0)

        new_cell = Cell()
        new_cell.rect.x = x
        new_cell.rect.y = y
        new_cell.index = (x_coord, int(y / 12))

        cells[x_coord].append(new_cell)
        all_sprites.add(new_cell)
        game_screen.blit(new_cell.surf, (x, y))

sprite_list = all_sprites.sprites()
sprite_list_len = len(sprite_list)
new_state = state
blank = state

while running:

    if ticking:
        logic()

    for event in pg.event.get():
        if event.type == pg.QUIT:
            running = False
        if event.type == pg.KEYDOWN:
            if event.key == pg.K_a:
                a = not a
            if event.key == pg.K_SPACE:
                if ticking:
                    ticking = not ticking
                else:
                    ticking = True
                print("ticking toggled to: ", ticking)

        if event.type == pg.MOUSEMOTION or pg.mouse.get_pressed()[0]:
            if pg.mouse.get_pressed()[0]:
                for sprites in all_sprites:
                    sprites.update(pg.mouse.get_pos(), a)

    for sprites in sprite_list:
        game_screen.blit(sprites.surf, (sprites.rect.x, sprites.rect.y))
    main_screen.blit(game_screen, (0, 0))
    pg.display.update(main_screen_rect)
    fps = pg.time.Clock()
    fps.tick(60)

Asked By: badprogrammer

||

Answers:

An assignment operation like new_state = state doesn’t generate a new object. After this expression, you have 2 variables that refer to the same object.
Actually you have just 1 grid of states. The variables state, new_state and blank refer to the same object. You must create a new and empty state grid in each frame:

def logic():
    global state, new_state
    new_state = [[0 for _ in row] for row in state]
    sum_calc()
    drawer()
    state = new_state

Furthermore, your algorithm is not correct. See Conway’s Game of Life. Change the lgoic:

def sum_calc():
    global new_state
    state_len = len(state)
    state_len_part = len(state[0])
    for x_c in range(1, state_len - 1):
        for y_c in range(1, state_len_part - 1):

            neigh_sum = 0
            for i in range(-1, 2):
                for j in range(-1, 2):
                    if x_c + i < state_len and y_c + j < len(state[x_c + i]):
                        neigh_sum += state[x_c + i][y_c + j]
            neigh_sum -= state[x_c][y_c]

            #if neigh_sum < 2 or neigh_sum > 3:
            #    new_state[x_c][y_c] = 0
            #elif neigh_sum == 3:
            #    new_state[x_c][y_c] = 1
            
            if state[x_c][y_c] == 1 and (neigh_sum == 2 or neigh_sum == 3):
                new_state[x_c][y_c] = 1
            elif state[x_c][y_c] == 0 and neigh_sum == 3:
                new_state[x_c][y_c] = 1
            else:
                new_state[x_c][y_c] = 0

Answered By: Rabbid76
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.