Игра жизни Конвея в PyGame не генерирует правильные шаблоны

Я пытаюсь сделать игру жизни Конвея на питоне, используя pygame. Я не могу понять, почему, но модели, которые он генерирует, далеки. Я просмотрел код миллион раз, и я не могу понять, что не так.

Вот скриншот одного из поколений gameoflife

А вот и полный код игры.

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)

Я не думаю, что проблема связана с функцией findNeighbors, поскольку я получил ее из этот ответ stackoverflow. Поэтому я предполагаю, что проблема в функции обновления, но я не вижу, где логика неверна, исходя из правил игры.


person Petefic    schedule 08.05.2014    source источник
comment
В игре «Жизнь» вы не можете многократно обновлять текущий экран. Каждая обновленная ячейка немедленно влияет на своих соседей, бит правее и на следующей строке. Создайте обновленный экран в другом буфере, чтобы предотвратить это.   -  person Jongware    schedule 09.05.2014
comment
У меня нет времени проверять это, но похоже, что вы изменяете массив cells во время подсчета. Если я правильно помню, вам нужно определить модификацию на основе исходного массива cells.   -  person DSM    schedule 09.05.2014


Ответы (1)


Подозреваю, что проблема в этом:

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

У вас есть только одна сетка, которую вы обновляете во время вычислений из нее. Поколение n+1 должно учитывать информацию только от поколения n, тогда как в настоящее время у вас есть смесь информации от n и n+1. Вы получите новые значения соседей сверху и слева от вас и старые значения соседей справа и снизу от вас, потому что они еще не пересчитывали.

Так, например, возьмите этот шаблон (где # означает «живой»):

...
###
...

Это должно идти по адресу:

.#.
.#.
.#.

... но на самом деле вы получите (я думаю):

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

К тому времени, когда мы вычисляем верхний правый угол, у него уже три соседа. Лево-средний имеет 2 живых соседа во время вычисления; в центре их 4, а в правом среднем — 2. Ничто в нижней строке не имеет трех соседей к моменту вычисления, поэтому оно остается мертвым.

Обычно реализация Conway's Life либо вычисляет совершенно новую сетку для каждого поколения, либо переключается между двумя сетками, вычисляя одну из другой.

person Jon Skeet    schedule 08.05.2014