0

I made a Game of Life to try out PyGame, but it stops rendering the bottom and right edges.

Not sure where to look even really I can't find anything in the webs.

Complete code posted so you can see what I mean.

import pygame
import random
pygame.init()

HEIGHT = 850 # Window Size
WIDTH = 850

l = 200 # cells per row
s = []

for i in range(l):
    s.append([])
    for j in range(l):
        if i % 2 == 0:
            s[i].append(1)
        else:
            s[i].append(0)

for i in range((l*l)/2):
    i = random.randrange(l)
    j = random.randrange(l)
    s[i][j] = not s[i][j]

SEED = s

BLACK    = (   0,   0,   0)
WHITE    = ( 255, 255, 255)
BLUE     = (   0,   0, 255)
GREEN    = (   0, 255,   0)
RED      = ( 255,   0,   0)

size = (WIDTH, HEIGHT)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("Conway's Game of Liff")

'''
CLASSES
'''

class Cell():
    def __init__(self, x, y, alive):
        self.x = x
        self.y = y
        self.w = WIDTH / len(SEED[0])
        self.h = HEIGHT / len(SEED)
        self.alive = alive

    def draw(self):
        x = self.x
        y = self.y
        h = self.h
        w = self.w
        pygame.draw.rect(screen, WHITE, pygame.Rect(x, y, h, w))

'''
Field Class - 2-d array of cells, updates each cell according to Evolve()
'''
class Field():
    def __init__(self, blank):
        temp = Cell(0, 0, False)
        w = temp.w
        h = temp.h
        del temp

        #sets all cells to false
        self.cells = [[Cell(i*w, j*h, False) for i in range(len(SEED))] for j in range(len(SEED[0]))]

        if not blank:
            for i in range(l):
                for j in range(l):
                    if SEED[i][j] == 1:
                        self.cells[i][j].alive = True

    def Evolve(self, i, j, temp):
        living = 0
        lim = l-1

        if i < lim:
            if self.cells[i+1][j].alive:
                living += 1
        if i < lim and j < lim:
            if self.cells[i+1][j+1].alive:
                living += 1
        if i > 0:
            if self.cells[i-1][j].alive:
                living += 1
        if j < lim:
            if self.cells[i][j+1].alive:
                living += 1
        if j > 0:
            if self.cells[i][j-1].alive:
                living += 1
        if i < lim and j > 0:
            if self.cells[i+1][j-1].alive:
                living += 1
        if i > 0 and j < lim:
            if self.cells[i-1][j+1].alive:
                living += 1
        if i > 0 and j > 0:
            if self.cells[i-1][j-1].alive:
                living += 1

        if self.cells[i][j].alive and (living < 2 or living > 3):
            temp.cells[i][j].alive = False
        elif (not self.cells[i][j].alive) and (living == 3):
            temp.cells[i][j].alive = True
        else:
            temp.cells[i][j].alive = self.cells[i][j].alive

    def drawLines(self):
        temp = Cell(0, 0, False)
        w = temp.w
        h = temp.h
        del temp

        for i in range(l):
            pygame.draw.line(screen, BLACK, (0, i*h), (WIDTH, i*h))
            pygame.draw.line(screen, BLACK, (i*w, 0), (i*w, HEIGHT))

    def update(self):
        temp_field = Field(True)
        imax = l
        jmax = l

        # populate buffer field
        for i in range(imax):
            for j in range(jmax):
                self.Evolve(i, j, temp_field)

        # copy buffer
        self.cells = temp_field.cells
        del temp_field

        for i in range(imax):
            for j in range(jmax):
                if self.cells[i][j].alive:
                    self.cells[i][j].draw()

        self.drawLines()



'''
MAIN STUFF
'''
#Loop until the user clicks the close button.
done = False
clock = pygame.time.Clock()
f = Field(False)

while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    screen.fill(BLUE) # wipe buffer

    f.update()
    pygame.display.flip() # flip buffer to screen
    clock.tick(5)

pygame.quit()

It's strange, regardless of the value of l (line 7) the edges don't draw cells or lines. Is this a problem with my code or with python?

I've never done Game of Life before, so any input on the mechanics are welcome too.

EDIT/SOLUTION: All the rounding was throwing everything off.

size = ((WIDTH/l) * l, (HEIGHT/l) * l)
screen = pygame.display.set_mode(size)
Will
  • 4,299
  • 5
  • 32
  • 50
  • 1
    I know this solves not really your problem, but if you use Python 3 you need to change `range((l*l)/2)` to `range((l*l)//2)` because the argument passed to the `range` constructor must be an integer. :) – elegent Apr 10 '15 at 17:23
  • 1
    Thought: in the line "self.w = WIDTH / len(SEED[0])", that's 850/200, which equals 4.25. However, this will be integer division, so self.w will equal 4. In other words, you might be drawing all 200 rows/columns, it's just that there will be a 50 pixel border on the right/bottom. – user3757614 Apr 10 '15 at 17:33
  • tangential to the main point: bad idea to have a variable named `l`. with some text editors, l is indistinguishable from 1. even when they are distinguishable, distinguishing them is difficult. – abcd Apr 11 '15 at 05:26

1 Answers1

2

As @user3757614 already posted the problem is that you use an integer division to calculate the w and h of each Cell instance:

self.w = WIDTH / len(SEED[0])
self.h = HEIGHT / len(SEED)

In Python 2.7 the / operator is an integer division if the inputs are integers too, therefore w and h will always be rounded down (i.e. decimal point cut off) and, depending on the value of l, there will be a border on the right and bottom side of your screen.

There are a number of ways to force division to be floating point in Python 2. One is making one of the parameters for division in floating point format, because this produces also a floating point output:

self.w = float(WIDTH) / len(SEED[0])
self.h = float(HEIGHT) / len(SEED)

Note that in Python 3 / is a float division, so you don´t need to change your calculation of w and h. But you need to change range((l*l)/2) to range((l*l)//2) because the argument passed to the range constructor must be an integer.

I hope this helps :)

Community
  • 1
  • 1
elegent
  • 3,857
  • 3
  • 23
  • 36
  • Thanks everybody, it was all in the rounding. easily fixed with {size = ((WIDTH/l) * l, (HEIGHT/l) * l);screen = pygame.display.set_mode(size);} – Will Apr 11 '15 at 05:16
  • Adding `from __future__ import division` is another alternative – warvariuc Apr 11 '15 at 05:28