Conway生命游戏滑翔机运行几代后失效,请求问题排查
Conway's生命游戏滑翔机失效问题排查
问题描述
未参考教程,用Python结合Pygame实现了Conway's生命游戏。程序整体看似正常,但滑翔机(Glider)运行几代后就会失效。尝试过相关帖子的解决方案无效,希望定位问题。预期滑翔机能正常运作,实际几代后失效。
代码实现
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
问题根源
1. 状态转换逻辑不完整(核心原因)
在cell.py的UpdateState方法中,仅处理了存活细胞死亡和死亡细胞复活的情况,未处理:
- 存活细胞邻居数为2/3时,应保持存活(未设置
self.nextState = 1) - 死亡细胞邻居数不为3时,应保持死亡(未设置
self.nextState = 0)
这导致nextState会保留上一轮的旧值,错误地改变细胞状态,直接导致滑翔机的细胞无故死亡,最终失效。
2. 多余的调试绘图代码
UpdateState中的pygame.draw.rect(screen, (0,0,0), rect)会在每帧绘制覆盖细胞的黑框,干扰视觉效果,且无实际功能。
3. 边界处理缺失(可选)
当前邻居查找逻辑仅能找到网格内的细胞,若滑翔机飞到边缘,边界外的细胞会被视为不存在(即死亡),导致滑翔机因邻居不足而死亡。若需要滑翔机在边缘环绕移动,需添加边界环绕处理。
修复方案
方案1:修复核心状态转换逻辑
修改cell.py的UpdateState方法,补全所有状态转换情况,并移除调试代码:
def UpdateState(self): rect = pygame.Rect(self.position.x-self.size, self.position.y-self.size, self.size*3, self.size*3) 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 < 2 or livingAmt > 3: self.nextState = 0 else: # 存活细胞满足条件,保持存活 self.nextState = 1 else: if livingAmt == 3: self.nextState = 1 else: # 死亡细胞不满足复活条件,保持死亡 self.nextState = 0
方案2:添加边界环绕功能(可选)
若希望滑翔机在网格边缘环绕移动,需修改网格存储结构和邻居查找逻辑:
- 修改
grid.py的Grid类,改用二维列表存储细胞,并记录行列数:
class Grid: def __init__(self, size): self.cellSize = size self.generations = 0 self.tick = 1 self.started = False # 计算网格行列数 self.cols = screen.get_width() // size self.rows = screen.get_height() // size self.GenerateGrid() def GenerateGrid(self): # 用二维列表存储细胞,方便按坐标查找 self.cells = [[None for _ in range(self.cols)] for _ in range(self.rows)] for y in range(self.rows): for x in range(self.cols): pos_x = x * self.cellSize pos_y = y * self.cellSize # 传递细胞在网格中的坐标(x,y) c = cell.Cell(self, (pos_x, pos_y), self.cellSize, (x, y)) self.cells[y][x] = c
- 修改
cell.py的Cell类,添加网格坐标属性,并修改UpdateState方法:
class Cell: def __init__(self, grid, position:tuple, size, grid_pos:tuple): 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.grid_pos = grid_pos # 记录细胞在网格中的坐标(x,y) self.state = 0 self.nextState = self.state # Draw方法保持不变... def UpdateState(self): x, y = self.grid_pos rows = self.grid.rows cols = self.grid.cols # 定义8个方向的邻居相对坐标 neighbor_dirs = [ (-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1) ] livingAmt = 0 for dx, dy in neighbor_dirs: # 边界环绕:取模运算实现 torus 拓扑 nx = (x + dx) % cols ny = (y + dy) % rows neighbor = self.grid.cells[ny][nx] if neighbor.state == 1: livingAmt += 1 # 补全状态转换逻辑 if self.state == 1: self.nextState = 1 if 2 <= livingAmt <=3 else 0 else: self.nextState = 1 if livingAmt ==3 else 0
验证
修复后,滑翔机的细胞状态会正确遵循生命游戏规则,不会无故死亡;若添加了边界环绕,滑翔机碰到边缘后会从对面重新出现,持续移动。
内容的提问来源于stack exchange,提问作者Kesh S
相关产品推荐
相关产品推荐

