[英]Unexpected results in Conway's Game of Life
I have been trying to write my own version of Conway's Game of Life as practice for Python using Pygame. 我一直在尝试使用Pygame编写自己的版本的Conway的《人生游戏》作为Python的练习。 I first wrote the functions for initializing the game field, and then calculating the next generation.
我首先编写了用于初始化游戏场然后计算下一代的函数。 I verified it's functionality using the console to print the results and verify that they returned the expected results (just 2 generations deep by hand on a 5x5 grid).
我使用控制台验证了它的功能,以打印结果并验证它们是否返回了预期的结果(在5x5的网格上只有2代的深度)。
An important note of how I am calculating the neighbors... Instead of doing a for loop through the entire array and doing for loops to count for each cell, I have implemented an array that holds the neighbor counts. 关于如何计算邻居的重要说明...我没有实现遍历整个数组的for循环和对每个单元进行计数的for循环,而是实现了一个保存邻居计数的数组。 Only making changes when a cells status is changed.
仅在单元格状态更改时进行更改。 This means I don't waste time calculating neighbors for cells that have not changed.
这意味着我不会浪费时间为未更改的单元计算邻居。
When it came time to use Pygame to display the array with rectangles, I wrote the following program. 当使用Pygame显示带有矩形的数组时,我编写了以下程序。 At first I was drawing the screen by filling in the entire screen white, and then drawing the live cells as black (this can be done by commenting out the else statement in update()).
首先,我是通过将整个屏幕填充为白色来绘制屏幕,然后将活动单元绘制为黑色(这可以通过在update()中注释else语句来完成)。 I expected this to work as normal, but when I ran the program all I ended up with was the screen filling in black.
我希望它能正常工作,但是当我运行该程序时,最终得到的是黑色屏幕。
I was perplexed by the result so i drew white rectangles for the unpopulated cells (using the else statement. And got a better looking result, but instead of the cells eventually all dying, they eventually multiplied across the whole screen. This is opposite of what I expected, as I was expecting it to eventually stabilize. 我对结果感到困惑,于是我为未填充的单元格绘制了白色矩形(使用else语句。并得到了更好看的结果,但是最终单元格在整个屏幕上成倍增加,而不是单元格最终全部死亡。我期望,因为我期望它最终会稳定下来。
Anyone know what I am doing wrong? 有人知道我在做什么错吗? I know that this is not the best way of writing this program, I welcome comments of how I can make it better.
我知道这不是编写此程序的最佳方法,我欢迎提出有关如何使其变得更好的意见。
- RETURN = run simulation
RETURN =运行模拟
- 'R' = randomize
'R'=随机
- 'T' = tick one generation
'T'=勾选一代
- 'C' = clear game field
'C'=清除比赛场地
- 'N' = display neighbor map
'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)
self.neighborsOld = self.neighbors
does not copy the map, it only points to it. self.neighborsOld = self.neighbors
不会复制地图,它只会指向地图。
See : 见:
a = [[1,2],[3,4]]
b = a
b[0][0] = 9
>>> a
[[9, 2], [3, 4]]
You need to either make a copy ( a[:]
) for every row in a
, or use the copy
module and use deepcopy
: 你需要要么使复印件(
a[:]
),在每一行a
,或者使用copy
模块,并使用deepcopy
:
b = [x[:] for x in a]
or 要么
import copy
b = copy.deepcopy(a)
Either way, it results in 无论哪种方式,它都会导致
b[0][0] = 9
>>> a
[[1, 2], [3, 4]]
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.