Conway的生命游戏中的意外结果

时间:2014-02-03 16:52:24

标签: python pygame conways-game-of-life

我一直在尝试使用Pygame编写我自己版本的Conway的生命游戏作为Python的练习。我首先编写了初始化游戏领域的函数,然后计算下一代。我使用控制台验证了它的功能,打印结果并验证它们是否返回了预期的结果(在5x5网格上只需2代深度)。

关于如何计算邻居的一个重要注意事项...我已经实现了一个保存邻居计数的数组,而不是在整个数组中执行for循环并为每个单元执行循环计数。仅在更改单元状态时进行更改。这意味着我不会浪费时间计算没有改变的单元格的邻居。

当使用Pygame显示带有矩形的数组时,我编写了以下程序。起初我通过填充整个屏幕白色来绘制屏幕,​​然后将活细胞绘制为黑色(这可以通过在update()中注释掉else语句来完成)。我希望这可以正常工作,但当我运行程序时,我最终得到的是屏幕填充为黑色。

我对结果感到困惑所以我为未填充的细胞绘制了白色矩形(使用else语句。并且得到了更好看的结果,但是最终所有细胞都死了,它们最终在整个屏幕上成倍增加。这是与我的预期相反,因为我期待它最终稳定下来。

任何人都知道我做错了什么?我知道这不是编写这个程序的最好方法,我欢迎评论如何让它变得更好。


  
      
  • RETURN =运行模拟
  •   
  • 'R'=随机化
  •   
  • 'T'=勾选一代
  •   
  • 'C'=清晰的游戏领域
  •   
  • 'N'=显示邻居地图
  •   
import pygame
from pygame.locals import *
import numpy as np
from random import *
import copy

fieldSize = [100,50]
cellSize = 10  # size of >10 is recommended to see neighbor count
windowSize = [fieldSize[0]*cellSize, fieldSize[1]*cellSize]

# calculate the last cell in each axis so it is not done repeatedly
lastCell = [(fieldSize[0]-1), (fieldSize[1]-1)]

dX = float(windowSize[0])/float(fieldSize[0])
dY = float(windowSize[1])/float(fieldSize[1])

colorAlive = [0,125,0]
colorDead = [0, 0, 0]

# todo list
# 1. make cLife take in the field size
# 2. convert random functions to numpy.random.randint

class cLife():
    def randomize(self):
        self.neighbors = np.zeros(fieldSize)
        # fill in the game field with random numbers
        for x in range(fieldSize[0]):
            for y in range(fieldSize[1]):
                if(randint(0,99)<20):
                    self.gameField[x][y] = 1
                    self.updateNeighbors([x,y], True)
                else:
                    self.gameField[x][y] = 0

    def displayNeighbors(self, surface):
        self.drawField(surface)
        for x in range(fieldSize[0]):
            for y in range(fieldSize[1]):
                neighborCount=font.render(str(int(self.neighbors[x][y])), 1,(200,200,200))
                surface.blit(neighborCount, (x*dX+dX/3, y*dY+dY/3.5))
        pygame.display.flip()

    # This is the function to update the neighbor map, the game field is torroidal so the complexity is greatly
    # increased. I have handcoded each instruction to avoid countless if statements and for loops.
    # Hopefully, this has drastically improved the performance. Using this method also allows me to avoid calculating
    # the neighbor map for every single cell because the neighbor map is updated only for the cells affected by a change.
    def updateNeighbors(self, pos, status):
        if(status == True):
            change = 1
        else:
            change = -1

        # testing for the cells in the center of the field (most cells are in the center so this is first)
        # cells are filled in starting on the top-left corner going clockwise
        if((pos[0]>0 and pos[0]<lastCell[0])and(pos[1]>0 and pos[1]<lastCell[1])):
            self.neighbors[pos[0]-1][pos[1]-1] += change
            self.neighbors[pos[0]][pos[1]-1] += change
            self.neighbors[pos[0]+1][pos[1]-1] += change
            self.neighbors[pos[0]+1][pos[1]] += change
            self.neighbors[pos[0]+1][pos[1]+1] += change
            self.neighbors[pos[0]][pos[1]+1] += change
            self.neighbors[pos[0]-1][pos[1]+1] += change
            self.neighbors[pos[0]-1][pos[1]] += change

        elif(pos[0] == 0): # left edge
            if(pos[1] == 0): # top left corner
                self.neighbors[lastCell[0]][lastCell[1]] += change
                self.neighbors[0][lastCell[1]] += change
                self.neighbors[1][lastCell[1]] += change
                self.neighbors[1][0] += change
                self.neighbors[1][1] += change
                self.neighbors[0][1] += change
                self.neighbors[lastCell[0]][1] += change
                self.neighbors[lastCell[0]][0] += change
            elif(pos[1] == lastCell[1]): # bottom left corner
                self.neighbors[lastCell[0]][pos[1]-1] += change
                self.neighbors[0][pos[1]-1] += change
                self.neighbors[1][pos[1]-1] += change
                self.neighbors[1][pos[1]] += change
                self.neighbors[1][0] += change
                self.neighbors[0][0] += change
                self.neighbors[lastCell[0]][0] += change
                self.neighbors[lastCell[0]][pos[1]] += change
            else: # everything else
                self.neighbors[lastCell[0]][pos[1]-1] += change
                self.neighbors[0][pos[1]-1] += change
                self.neighbors[1][pos[1]-1] += change
                self.neighbors[1][pos[1]] += change
                self.neighbors[1][pos[1]+1] += change
                self.neighbors[0][pos[1]+1] += change
                self.neighbors[lastCell[0]][pos[1]+1] += change
                self.neighbors[lastCell[0]][pos[1]] += change

        elif(pos[0] == lastCell[0]): # right edge
            if(pos[1] == 0): # top right corner
                self.neighbors[pos[0]-1][lastCell[1]] += change
                self.neighbors[pos[0]][lastCell[1]] += change
                self.neighbors[0][lastCell[1]] += change
                self.neighbors[0][0] += change
                self.neighbors[0][1] += change
                self.neighbors[pos[0]][1] += change
                self.neighbors[pos[0]-1][1] += change
                self.neighbors[pos[0]-1][0] += change
            elif(pos[1] == lastCell[1]): # bottom right corner
                self.neighbors[pos[0]-1][pos[1]-1] += change
                self.neighbors[pos[0]][pos[1]-1] += change
                self.neighbors[0][pos[1]-1] += change
                self.neighbors[0][pos[1]] += change
                self.neighbors[0][0] += change
                self.neighbors[pos[0]][0] += change
                self.neighbors[pos[0]-1][0] += change
                self.neighbors[pos[0]-1][pos[1]] += change
            else: # everything else
                self.neighbors[pos[0]-1][pos[1]-1] += change
                self.neighbors[pos[0]][pos[1]-1] += change
                self.neighbors[0][pos[1]-1] += change
                self.neighbors[0][pos[1]] += change
                self.neighbors[0][pos[1]+1] += change
                self.neighbors[pos[0]][pos[1]+1] += change
                self.neighbors[pos[0]-1][pos[1]+1] += change
                self.neighbors[pos[0]-1][pos[1]] += change

        elif(pos[1] == 0): # top edge, corners already taken care of
            self.neighbors[pos[0]-1][lastCell[1]] += change
            self.neighbors[pos[0]][lastCell[1]] += change
            self.neighbors[pos[0]+1][lastCell[1]] += change
            self.neighbors[pos[0]+1][0] += change
            self.neighbors[pos[0]+1][1] += change
            self.neighbors[pos[0]][1] += change
            self.neighbors[pos[0]-1][1] += change
            self.neighbors[pos[0]-1][0] += change

        elif(pos[1] == lastCell[1]): # bottom edge, corners already taken care of
            self.neighbors[pos[0]-1][pos[1]-1] += change
            self.neighbors[pos[0]][pos[1]-1] += change
            self.neighbors[pos[0]+1][pos[1]-1] += change
            self.neighbors[pos[0]+1][pos[1]] += change
            self.neighbors[pos[0]+1][0] += change
            self.neighbors[pos[0]][0] += change
            self.neighbors[pos[0]-1][0] += change
            self.neighbors[pos[0]-1][pos[1]] += change

    def nextGeneration(self):
        # copy the neighbor map, because changes will be made during the update
        self.neighborsOld = copy.deepcopy(self.neighbors)

        for x in range(fieldSize[0]):
            for y in range(fieldSize[1]):
                # Any live cell with fewer than two live neighbours dies, as if caused by under-population.
                if(self.gameField[x][y] == 1 and self.neighborsOld[x][y] < 2):
                    self.gameField[x][y] = 0
                    self.updateNeighbors([x,y], False)
                # Any live cell with more than three live neighbours dies, as if by overcrowding.
                elif(self.gameField[x][y] == 1 and self.neighborsOld[x][y] >3):
                    self.gameField[x][y] = 0
                    self.updateNeighbors([x,y], False)
                # Any dead cell with exactly three live neighbours becomes a live cell, as if by reproduction.
                elif(self.gameField[x][y] == 0 and self.neighborsOld[x][y] == 3):
                    self.gameField[x][y] = 1
                    self.updateNeighbors([x,y], True)

    def drawField(self, surface):
        surface.fill(colorDead)

        # loop through and draw each live cell
        for x in range(fieldSize[0]):
            for y in range(fieldSize[1]):
                if(self.gameField[x][y] == 1):
                    pygame.draw.rect(surface, colorAlive, [dX*x, dY*y, dX, dY])

        pygame.display.flip()

    def __init__(self):
        # initialize the game field and neighbor map with zeros
        self.gameField = np.zeros(fieldSize)
        self.neighbors = np.zeros(fieldSize)


# begining of the program
game = cLife()

pygame.init()
surface = pygame.display.set_mode(windowSize)
pygame.display.set_caption("Conway\'s Game of Life")
clock = pygame.time.Clock()
pygame.font.init()
font=pygame.font.Font(None,10)

surface.fill(colorDead)
game.randomize()
game.drawField(surface)
pygame.display.flip()

running = False

while True:
    #clock.tick(60)

    # handling events
    for event in pygame.event.get():
        if(event.type == pygame.MOUSEBUTTONDOWN):
            mousePos = pygame.mouse.get_pos()
            x = int(mousePos[0]/dX)
            y = int(mousePos[1]/dY)

            if(game.gameField[x][y] == 0):
                game.gameField[x][y] = 1
                game.updateNeighbors([x, y], True)
                game.drawField(surface)
            else:
                game.gameField[x][y] = 0
                game.updateNeighbors([x, y], False)
                game.drawField(surface)

        elif(event.type == pygame.QUIT):
            pygame.quit()
        elif(event.type == pygame.KEYDOWN):
            # return key starts and stops the simulation
            if(event.key == pygame.K_RETURN):
                if(running == False):
                    running = True
                else:
                    running = False
            # 't' key ticks the simulation forward one generation
            elif(event.key == pygame.K_t and running == False):
                game.nextGeneration()
                game.drawField(surface)
            # 'r' randomizes the playfield
            elif(event.key == pygame.K_r):
                game.randomize()
                game.drawField(surface)
            # 'c' clears the game field
            elif(event.key == pygame.K_c):
                running = False
                game.gameField = np.zeros(fieldSize)
                game.neighbors = np.zeros(fieldSize)
                game.drawField(surface)
            # 'n' displays the neighbor map
            elif(event.key == pygame.K_n):
                game.displayNeighbors(surface)

    if(running == True):
        game.nextGeneration()
        game.drawField(surface)

1 个答案:

答案 0 :(得分:1)

self.neighborsOld = self.neighbors不会复制地图,只会指向它。

见:

a = [[1,2],[3,4]]
b = a
b[0][0] = 9
>>> a
[[9, 2], [3, 4]]

您需要为a[:]中的每一行制作副本(a),或使用copy模块并使用deepcopy

b = [x[:] for x in a]

import copy
b = copy.deepcopy(a)

无论哪种方式,都会导致

b[0][0] = 9
>>> a
[[1, 2], [3, 4]]