Search code examples
pythonpygameconways-game-of-life

Conway's Game of Life Gliders break after a few generations


So I tried creating Conway's game of life in python with pygame. I made this without watching any tutorials, which is probably why it is so broken. It seems to be working fine, but when I creates a glider it seems to just break after a few generations. I looked at some other posts about my problem and added their solutions but that didn't make it work either. I know this is a lot to ask for, but can someone at least identify the problem.

Here is my code. I expected the glider to function as do they are supposed to, but it ended up just breaking in a few generations

Code: main.py:

from utils import *
from grid import Grid


running = True
t = Grid(30)

while running:
    pygame.display.set_caption(f'Conways Game of Life <Gen {t.generations}>')
    clock.tick(200)
    screen.fill(background_colour)

    if not t.started:
        t.EditMode()
    else:
        t.Update()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
    pygame.display.flip()`

grid.py:

import cell
from utils import *

class Grid:
    def __init__(self, size):
        self.cells = []
        self.cellSize = size
        self.generations = 0
        self.tick = 1
        self.started = False
        self.GenerateGrid()
    
    def GenerateGrid(self):
        x, y = 0, 0

        while y < screen.get_height():
            while x < screen.get_width():
                c = cell.Cell(self, (x,y), self.cellSize)

                self.cells.append(c)
                x+=self.cellSize

            x = 0
            y+=self.cellSize

    def EditMode(self):
        self.Draw()
        if self.started:
            return
        
        for cell in self.cells:
            if pygame.mouse.get_pressed()[0]:
                if cell.rect.collidepoint(pygame.mouse.get_pos()):
                    cell.state = 1
            if pygame.mouse.get_pressed()[2]:
                if cell.rect.collidepoint(pygame.mouse.get_pos()):
                    cell.state = 0

        keys = pygame.key.get_pressed()

        if keys[pygame.K_RETURN]:
            self.started = True

    def Draw(self):
        for cell in self.cells:
            cell.Draw()

    def Update(self):
        self.Draw()
        self.tick -= 0.05

        if self.tick < 0:
            for cell in self.cells:
                cell.UpdateState()
            
            for cell in self.cells:
                cell.state = cell.nextState
            self.tick = 1
            self.generations+=1

cell.py

from utils import *


class Cell: 
    def __init__(self, grid, position:tuple, size):
        self.grid = grid
        self.size = size
        self.position = pygame.Vector2(position[0], position[1])
        self.rect = pygame.Rect(self.position.x, self.position.y, self.size, self.size)
        self.state = 0
        self.nextState = self.state

    def Draw(self):
        pygame.draw.rect(screen, (0,0,0), self.rect)

        if self.state == 0:
            pygame.draw.rect(screen, (23,23,23), (self.position.x+4, self.position.y+4, self.size-4, self.size-4))
        else:
            pygame.draw.rect(screen, (255,255,255), (self.position.x+4, self.position.y+4, self.size-4, self.size-4))
    
    def UpdateState(self):
        rect = pygame.Rect(self.position.x-self.size, self.position.y-self.size, self.size*3, self.size*3)
        pygame.draw.rect(screen, (0,0,0), rect)
        targetCells = []
        
        for c in self.grid.cells:
            if rect.colliderect(c.rect):
                targetCells.append(c)


        livingAmt = 0
        for c in targetCells:
            if c.rect.x == self.rect.x and c.rect.y == self.rect.y:
                continue
            
            if c.state == 1:
                livingAmt+=1

        if self.state == 1:
            if livingAmt > 3 or livingAmt <2:
                self.nextState = 0
        if self.state ==0:
            if livingAmt == 3:
                self.nextState =1

utils.py

import pygame

background_colour = (23, 23, 23)
screen = pygame.display.set_mode((900, 900))
clock = pygame.time.Clock()

running = True

Solution

  • Your function UpdateState both counts a cell's neighbors and updates the cell's state. Since you call that function in a loop, both are done together, which does not work, as explained here. You must split the "count" phase from the "update state" phase.