Game of Life - 覆盖当前一代而不是更新到下一代

问题描述

下面我添加了我的生命密码游戏.规则定义正确,运行顺畅.但是,游戏并没有按预期运行.它没有更新到下一代,但似乎正在覆盖当前一代.举个例子:三个水平点应该在下一代变成三个垂直点,但这不会发生.

解决办法:我有两代人,现在和下一代.它必须将规则应用于当前一代并在下一代更新它们.然后它必须一次性用下一代覆盖当前一代,而不是一个单元一个单元.我该如何解决这个问题?

将 tkinter 导入为 tk导入 itertools、操作系统、平台、pygame、随机# 定义网格尺寸.GRID_SIZE = 宽度、高度 = 750、1000# 定义单元格大小和 X 和 Y 方向的单元格数.单元尺寸 = 10X_CELLS = int(WIDTH/CELL_SIZE)Y_CELLS = int(高度/CELL_SIZE)# 定义死细胞和活细胞的数量和颜色.颜色死亡 = 0COLOR_ALIVE = 1颜色 = []colors.append((0, 0, 0)) # 黑色colors.append((0, 128, 128)) # 蓝色# 定义两个列表:当前一代和下一代.current_generation = [[COLOR_DEAD for y in range(Y_CELLS)] for x in range(X_CELLS)]next_generation = [[COLOR_DEAD for y in range(Y_CELLS)] for x in range(X_CELLS)]# 定义游戏的每秒最大帧数/速度.FPS_MAX = 10类 GameOfLife:"""描述该方法的作用."""def __init__(self):# 初始化解释器并创建一个根窗口和标题.self.root = tk.Tk()self.root.title("生命游戏 - 创建者 - 玩得开心")# 定义主框架、左侧框架和右侧框架.self.frame = tk.Frame(self.root, width=1000, height=1000, highlightbackground='red')self.menu = tk.Frame(self.frame, width=250, height=1000, highlightbackground='#595959', highlightthickness=10)self.game_border = tk.Frame(self.frame, width=750, height=1000, highlightbackground='green', highlightthickness=10)# 包装窗户.self.frame.pack()self.frame.pack_propagate(0)self.menu.pack(side="left")self.menu.pack_propagate(0)self.game_border.pack()# 定义按钮.self.button_start = tk.Button(self.menu, text="Start", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80",命令=self.start_button)self.button_stop = tk.Button(self.menu, text="Stop", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80",命令=self.stop_button)self.button_iteration = tk.Button(self.menu, text="下一次迭代", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", 命令=self.create_next_gen)self.button_random = tk.Button(self.menu, text="Random", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80",命令=self.random_grid)self.button_reset = tk.Button(self.menu, text="Reset", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80",命令=self.reset_button)self.button_quit = tk.Button(self.menu, text="Quit", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80",命令=self.quit_button)# 打包按钮.self.button_start.pack()self.button_stop.pack()self.button_iteration.pack()self.button_random.pack()self.button_reset.pack()self.button_quit.pack()# 放置按钮.self.button_start.place(x=40, y=50)self.button_stop.place(x=40, y=200)self.button_iteration.place(x=40, y=350)self.button_random.place(x=40, y=500)self.button_reset.place(x=40, y=650)self.button_quit.place(x=40, y=800)# 定义滑块.self.slider_random = tk.Scale(self.menu, from_=0, to=100, orient="horizo​​ntal", command=self.slider_value)self.slider_random.set(50)# 包装滑块.self.slider_random.pack()# 放置滑块.self.slider_random.place(x=62, y=590)# 定义表单和颜色的下拉菜单."""self.options_figures = [界",正方形",惊喜"]self.var_figure = tk.StringVar(self.root)self.dropdown_figure = tk.OptionMenu(self.menu, self.var_figure,self.options_figures[0],self.options_figures[1],self.options_figures[2])self.var_figure.set(self.options_figures[0])#self.var_color.trace("w", FUNCTIONNAME)self.dropdown_figure.pack()# 单元格颜色的下拉菜单self.options_colors = [蓝色",红色的",白色的",绿色",黄色的",紫色的",灰色的",粉色的"]self.var_color = tk.StringVar(self.root)self.dropdown_colors = tk.OptionMenu(self.menu, self.var_color,self.options_colors[0],self.options_colors[1],self.options_colors[2],self.options_colors[3],self.options_colors[4],self.options_colors[5],self.options_colors[6], self.options_colors[7])self.var_color.set(self.options_colors[0])#self.var_color.trace("w", 函数名)self.dropdown_colors.pack()"""# 定义计算死细胞和活细胞的标签."""self.label_alive = tk.Label(self.menu, text="活细胞:"+" 1000", height=5, width=20, fg="black", background="grey80")self.label_dead = tk.Label(self.menu, text="死细胞"+" 1000", height=1, width=20, fg="black", background="grey80")包装标签self.label_alive.pack()self.label_dead.pack()self.label_alive.place(x=40, y=900)self.label_alive.place(x=40, y=900)"""# 这会将 pygame 窗口嵌入到 tkinter 框架中.os.environ['SDL_WINDOWID'] = str(self.game_border.winfo_id())系统 = 平台.system()如果系统 == Windows":os.environ['SDL_VIDEODRIVER'] = 'windib'elif 系统 == Linux":os.environ['SDL_VIDEODRIVER'] = 'x11'# 初始化 pygame.pygame.init()self.screen = pygame.display.set_mode(GRID_SIZE)# 初始化世代.self.init_gen(current_generation, COLOR_DEAD)# 定义一个时钟来设置 FPS.self.fps_clock = pygame.time.Clock()# 设置变量供以后使用.self.next_iteration = Falseself.game_over = False# 获取滑块值以更改随机性百分比.def 滑块值(自我,值):自我价值=价值# 按钮功能.def start_button(self):self.next_iteration = Truedef stop_button(self):self.next_iteration = Falsedef reset_button(self):self.next_iteration = Falseself.init_gen(next_generation, COLOR_DEAD)def 退出按钮(自我):self.game_over = True# 初始化所有单元格.def init_gen(self, generation, c):对于范围内的 y(Y_CELLS):对于范围内的 x(X_CELLS):世代[x][y] = c# 根据滑块值创建一个随机网格.def 随机网格(自我):self.next_iteration = Falseself.init_gen(next_generation, COLOR_DEAD)self.percentage_zero = list(itertools.repeat(0,(100 - self.slider_random.get())))self.percentage_one = list(itertools.repeat(1,(self.slider_random.get())))# 打印(self.percentage_zero)# 打印(self.percentage_one)对于范围内的行(X_CELLS):对于范围内的col(Y_CELLS):next_generation[row][col] = random.choice(self.percentage_zero + self.percentage_one)# print(next_generation[row][col])# 在 (x,y) 位置绘制单元格,颜色为黑色或蓝色.def draw_cell(self, x, y, c):pos = (int(x * CELL_SIZE + CELL_SIZE/2),int(y * CELL_SIZE + CELL_SIZE/2))# pygame.draw.rect(screen, colors[c], pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE-1, CELL_SIZE-1))# pygame.draw.circle(screen, colors[c], pos, CELL_SIZE, CELL_SIZE) #奇怪的形式,也可以用矩形代替pygame.draw.circle(self.screen, colors[c], pos, 5, 0)# 更新当前代的单元格.def update_gen(self):全球 current_generation对于范围内的 y(Y_CELLS):对于范围内的 x(X_CELLS):c = 下一代[x][y]self.draw_cell(x, y, c)current_generation = list(next_generation)# 激活一个活细胞.def activate_living_cell(自我,x,y):全球下一代下一代[x][y] = COLOR_ALIVE# 停用活细胞.def deactivate_living_cell(self, x, y):全球下一代下一代[x][y] = COLOR_DEAD# 检查邻居小区的功能.def check_cells(self, x, y):# 检查边缘.如果 (x < 0) 或 (y < 0):返回 0如果 (x >= X_CELLS) 或 (y >= Y_CELLS):返回 0如果 current_generation[x][y] == COLOR_ALIVE:返回 1别的:返回 0def check_cell_neighbors(self, row_index, col_index):# 获取当前cell周围的存活cell数量.num_alive_neighbors = 0num_alive_neighbors += self.check_cells(row_index - 1, col_index - 1)num_alive_neighbors += self.check_cells(row_index - 1, col_index)num_alive_neighbors += self.check_cells(row_index - 1, col_index + 1)num_alive_neighbors += self.check_cells(row_index, col_index - 1)num_alive_neighbors += self.check_cells(row_index, col_index + 1)num_alive_neighbors += self.check_cells(row_index + 1, col_index - 1)num_alive_neighbors += self.check_cells(row_index + 1, col_index)num_alive_neighbors += self.check_cells(row_index + 1, col_index + 1)返回 num_alive_neighbors# 规则:# 1 任何少于两个活邻居的活细胞都会死亡,就好像人口不足一样.# 2 任何有两三个活邻居的活细胞都可以活到下一代.# 3 任何有超过三个活邻居的活细胞都会死亡,就像人口过剩一样.# 4 任何只有三个活邻居的死细胞都会变成活细胞,就像通过繁殖一样.def create_next_gen(self):对于范围内的 y(Y_CELLS):对于范围内的 x(X_CELLS):n = self.check_cell_neighbors(x, y) # 邻居数.c = current_generation[x][y] # 当前单元格(死的或活的).如果 c == COLOR_ALIVE:if (n < 2): # 规则编号 1.下一代[x][y] = COLOR_DEADelif (n > 3): # 规则编号 3.下一代[x][y] = COLOR_DEADelse: # 规则 2.下一代[x][y] = COLOR_ALIVEelif c == COLOR_DEAD:if (n == 3): # 规则编号 4.下一代[x][y] = COLOR_ALIVE别的:下一代[x][y] = COLOR_DEAD#问题:首先计数,然后下一次迭代.# 定义按钮和鼠标点击.def 句柄事件(自我):对于 pygame.event.get() 中的事件:# 将鼠标位置转换为网格中的位置.posn = pygame.mouse.get_pos()x = int(posn[0]/CELL_SIZE)y = int(posn[1]/CELL_SIZE)# 按下退出 -->退出游戏.如果 event.type == pygame.QUIT:self.game_over = True# 按鼠标左键激活或取消激活一个单元格.如果 event.type == pygame.MOUSEBUTTONDOWN:如果 event.button == 1:如果下一代[x][y] == COLOR_DEAD:self.activate_living_cell(x, y)别的:self.deactivate_living_cell(x, y)# 按住鼠标右键激活绘图模式.如果 event.type == pygame.MOUSEMOTION 和 event.buttons[2]:self.activate_living_cell(x, y)# 定义q、空格、a、s、r的键盘按键.如果 event.type == pygame.KEYDOWN:# 退出游戏.如果 event.unicode == 'q':self.game_over = True打印(q")# 下一次迭代 - 手动.elif event.key == pygame.K_SPACE:self.create_next_gen()打印(按键")# 下一次迭代 - 自动化.elif event.unicode == 'a': #a 自动迭代.self.next_iteration = True打印(一个")# 停止自动迭代.elif event.unicode == 's':self.next_iteration = False印刷")# 清空网格.elif event.unicode == 'r':self.next_iteration = Falseself.init_gen(next_generation, COLOR_DEAD)打印(r")# 运行游戏循环定义运行(自我):而不是self.game_over:self.handle_events()如果 self.next_iteration:self.create_next_gen()self.update_gen()pygame.display.flip()self.fps_clock.tick(FPS_MAX)self.root.update()如果 __name__ == "__main__":游戏 = GameOfLife()游戏运行()

解决方案

你必须将 next_generation 深度复制到 current_generation.但是

<块引用>

current_generation = list(next_generation)

没有做你期望它做的事情,因为 next_generation 的元素也是一个列表.

要深度复制每个元素是数字列表的列表(不会复制对象),您必须:

current_generation = [list(e) for e in next_generation]

current_generation = [[i for i in j] for j in next_generation]

current_generation = [e[:] for e in next_generation]

由于GameOfLife.update_gen方法中有一个嵌套循环,这个问题也可以通过简单的赋值来解决:

类 GameOfLife:# [...]# 更新当前代的单元格.def update_gen(self):全球 current_generation对于范围内的 y(Y_CELLS):对于范围内的 x(X_CELLS):c = 下一代[x][y]self.draw_cell(x, y, c)current_generation[x][y] = next_generation[x][y] # 逐元素赋值

<小时>

还有一个问题,当动画正在运行并且游戏由鼠标操作时.按下鼠标时,next_generation 会被 .activate_living_cell.deactivate_living_cell 更改.
但之后 next_generationcurrent_generation 中的数据重新计算.

<块引用>

虽然不是self.game_over:self.handle_events() # 通过点击改变next_generation"如果 self.next_iteration:self.create_next_gen() # 从current_generation"计算next_generation"self.update_gen() # 从next_generation"复制current_generation"

这个问题可以轻松解决.考虑当游戏被鼠标操作时,current_generationnext_generation的内容是相等的.

在操作后更改 current_generation 而不是 next_generation 或更新 current_generation:

def handle_events(self):对于 pygame.event.get() 中的事件:# [...]# 按鼠标左键激活或取消激活一个单元格.如果 event.type == pygame.MOUSEBUTTONDOWN:如果 event.button == 1:如果下一代[x][y] == COLOR_DEAD:self.activate_living_cell(x, y)别的:self.deactivate_living_cell(x, y)self.update_gen() # <----------------------------

Below I have added my game of life code. The rules are defined correctly, and it runs smoothly. However, the game does not work as supposed. It is not updating to the next generation, but it seems to be overwriting the current generation. As an example: Three horizontal dots are supposed to turn into three vertical dots in the next generation, but this does not happen.

The solution: I have two generations, the current and the next generation. It has to apply the rules to the current generation and update them in the next generation. Then it has to overwrite the current generation with the next generation in one go, not cell by cell. How can I fix this?

import tkinter as tk
import itertools, os, platform, pygame, random

# Defining the grid dimensions.
GRID_SIZE = WIDTH, HEIGHT = 750, 1000

# Defining the cell size and the number of cells in the X and Y direction.
CELL_SIZE = 10
X_CELLS = int(WIDTH/CELL_SIZE)
Y_CELLS = int(HEIGHT/CELL_SIZE)

# Defining the number and color for dead and living cells.
COLOR_DEAD = 0
COLOR_ALIVE = 1
colors = []
colors.append((0, 0, 0))  # Black
colors.append((0, 128, 128))  # blue

# Defining two lists: current generation and next generation.
current_generation = [[COLOR_DEAD for y in range(Y_CELLS)] for x in range(X_CELLS)]
next_generation = [[COLOR_DEAD for y in range(Y_CELLS)] for x in range(X_CELLS)]

# Defining the max frames per second/speed of the game.
FPS_MAX = 10

class GameOfLife:
    """
    describe what the method does.
    """
    def __init__(self):
        # Initializing the interpreter and creating a root window and title.
        self.root = tk.Tk()
        self.root.title("Game of Life - Created by - Have fun")
        # Defining the main frame, left-side frame and right-side frame.
        self.frame = tk.Frame(self.root , width=1000, height=1000, highlightbackground='red')
        self.menu = tk.Frame(self.frame, width=250, height=1000, highlightbackground='#595959', highlightthickness=10)
        self.game_border = tk.Frame(self.frame, width=750, height=1000, highlightbackground='green', highlightthickness=10)
        # Packing the windows.
        self.frame.pack()
        self.frame.pack_propagate(0)
        self.menu.pack(side="left")
        self.menu.pack_propagate(0)
        self.game_border.pack()

        # Defining the buttons.
        self.button_start = tk.Button(self.menu, text="Start", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.start_button)
        self.button_stop = tk.Button(self.menu, text="Stop", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.stop_button)
        self.button_iteration = tk.Button(self.menu, text="Next iteration", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.create_next_gen)
        self.button_random = tk.Button(self.menu, text="Random", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.random_grid)
        self.button_reset = tk.Button(self.menu, text="Reset", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.reset_button)
        self.button_quit = tk.Button(self.menu, text="Quit", height=5, width=20, fg="black", activeforeground="red", background="grey80", activebackground="grey80", command=self.quit_button)
        # Packing the buttons.
        self.button_start.pack()
        self.button_stop.pack()
        self.button_iteration.pack()
        self.button_random.pack()
        self.button_reset.pack()
        self.button_quit.pack()
        # Placing the buttons.
        self.button_start.place(x=40, y=50)
        self.button_stop.place(x=40, y=200)
        self.button_iteration.place(x=40, y=350)
        self.button_random.place(x=40, y=500)
        self.button_reset.place(x=40, y=650)
        self.button_quit.place(x=40, y=800)

        # Defining the slider.
        self.slider_random = tk.Scale(self.menu, from_=0, to=100, orient="horizontal", command=self.slider_value)
        self.slider_random.set(50)
        # Packing the slider.
        self.slider_random.pack()
        # Placing the slider.
        self.slider_random.place(x=62, y=590)

        # Defining a dropdown menu for the form and color.
        """
        self.options_figures = [
            "circles",
            "squares",
            "surprise"
        ]
        self.var_figure = tk.StringVar(self.root)
        self.dropdown_figure = tk.OptionMenu(self.menu, self.var_figure,
                                             self.options_figures[0], self.options_figures[1],
                                             self.options_figures[2])
        self.var_figure.set(self.options_figures[0])
        #self.var_color.trace("w", FUNCTIONNAME)
        self.dropdown_figure.pack()

        # Dropdown menu for the cell color
        self.options_colors = [
            "blue",
            "red",
            "white",
            "green",
            "yellow",
            "purple",
            "grey",
            "pink"
        ]
        self.var_color = tk.StringVar(self.root)
        self.dropdown_colors = tk.OptionMenu(self.menu, self.var_color,
                                             self.options_colors[0], self.options_colors[1],
                                             self.options_colors[2], self.options_colors[3],
                                             self.options_colors[4], self.options_colors[5],
                                             self.options_colors[6], self.options_colors[7])
        self.var_color.set(self.options_colors[0])
        #self.var_color.trace("w", FUNCTION NAME)
        self.dropdown_colors.pack()
        """

        # Defining the labels that count the dead and living cells.
        """
        self.label_alive = tk.Label(self.menu, text="Living cells:"+" 1000", height=5, width=20, fg="black", background="grey80")
        self.label_dead = tk.Label(self.menu, text="Dead cells"+" 1000", height=1, width=20, fg="black", background="grey80")
        Packing the labels
        self.label_alive.pack()
        self.label_dead.pack()
        self.label_alive.place(x=40, y=900)
        self.label_alive.place(x=40, y=900)
        """

        # This embeds the pygame window in the tkinter frame.
        os.environ['SDL_WINDOWID'] = str(self.game_border.winfo_id())
        system = platform.system()
        if system == "Windows":
            os.environ['SDL_VIDEODRIVER'] = 'windib'
        elif system == "Linux":
            os.environ['SDL_VIDEODRIVER'] = 'x11'

        # Initializing pygame.
        pygame.init()
        self.screen = pygame.display.set_mode(GRID_SIZE)
        # Initializing the generations.
        self.init_gen(current_generation, COLOR_DEAD)
        # Defining a clock to set the FPS.
        self.fps_clock = pygame.time.Clock()
        # Setting variables for later use.
        self.next_iteration = False
        self.game_over = False

    # Get the slider value to change the % of randomness.
    def slider_value(self, value):
        self.value = value

    # Button functions.
    def start_button(self):
        self.next_iteration = True
    def stop_button(self):
        self.next_iteration = False
    def reset_button(self):
        self.next_iteration = False
        self.init_gen(next_generation, COLOR_DEAD)
    def quit_button(self):
        self.game_over = True

    # Initializing all the cells.
    def init_gen(self, generation, c):
        for y in range(Y_CELLS):
            for x in range(X_CELLS):
                generation[x][y] = c

    # Creates a random grid based on the slider value.
    def random_grid(self):
        self.next_iteration = False
        self.init_gen(next_generation, COLOR_DEAD)
        self.percentage_zero = list(itertools.repeat(0,
                                                     (100 - self.slider_random.get())))
        self.percentage_one = list(itertools.repeat(1,
                                                    (self.slider_random.get())))
        # print(self.percentage_zero)
        # print(self.percentage_one)
        for row in range(X_CELLS):
            for col in range(Y_CELLS):
                next_generation[row][col] = random.choice(self.percentage_zero + self.percentage_one)
                # print(next_generation[row][col])

    # Drawing the cells, color black or blue at location (x,y).
    def draw_cell(self, x, y, c):
        pos = (int(x * CELL_SIZE + CELL_SIZE / 2),
               int(y * CELL_SIZE + CELL_SIZE / 2))
        # pygame.draw.rect(screen, colors[c], pygame.Rect(x * CELL_SIZE, y * CELL_SIZE, CELL_SIZE-1, CELL_SIZE-1))
        # pygame.draw.circle(screen, colors[c], pos, CELL_SIZE, CELL_SIZE) #Weird form, can also be used instead of rectangles
        pygame.draw.circle(self.screen, colors[c], pos, 5, 0)

    # Updating the cells in the current generation.
    def update_gen(self):
        global current_generation
        for y in range(Y_CELLS):
            for x in range(X_CELLS):
                c = next_generation[x][y]
                self.draw_cell(x, y, c)
        current_generation = list(next_generation)

    # Activate a living cell.
    def activate_living_cell(self, x, y):
        global next_generation
        next_generation[x][y] = COLOR_ALIVE

    # Deactivate a living cell.
    def deactivate_living_cell(self, x, y):
        global next_generation
        next_generation[x][y] = COLOR_DEAD

    # Function to check neighbor cells.
    def check_cells(self, x, y):
        # Check the edges.
        if (x < 0) or (y < 0):
            return 0
        if (x >= X_CELLS) or (y >= Y_CELLS):
            return 0
        if current_generation[x][y] == COLOR_ALIVE:
            return 1
        else:
            return 0

    def check_cell_neighbors(self, row_index, col_index):
        # Get the number of alive cells surrounding the current cell.
        num_alive_neighbors = 0
        num_alive_neighbors += self.check_cells(row_index - 1, col_index - 1)
        num_alive_neighbors += self.check_cells(row_index - 1, col_index)
        num_alive_neighbors += self.check_cells(row_index - 1, col_index + 1)
        num_alive_neighbors += self.check_cells(row_index, col_index - 1)
        num_alive_neighbors += self.check_cells(row_index, col_index + 1)
        num_alive_neighbors += self.check_cells(row_index + 1, col_index - 1)
        num_alive_neighbors += self.check_cells(row_index + 1, col_index)
        num_alive_neighbors += self.check_cells(row_index + 1, col_index + 1)
        return num_alive_neighbors

    # Rules:
    # 1 Any live cell with fewer than two live neighbors dies, as if by underpopulation.
    # 2 Any live cell with two or three live neighbors lives on to the next generation.
    # 3 Any live cell with more than three live neighbors dies, as if by overpopulation.
    # 4 Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.
    def create_next_gen(self):
        for y in range(Y_CELLS):
            for x in range(X_CELLS):
                n = self.check_cell_neighbors(x, y)  # Number of neighbors.
                c = current_generation[x][y]  # Current cell (either dead or alive).
                if c == COLOR_ALIVE:
                    if (n < 2):  # Rule number 1.
                        next_generation[x][y] = COLOR_DEAD
                    elif (n > 3):  # Rule number 3.
                        next_generation[x][y] = COLOR_DEAD
                    else:  # Rule number 2.
                        next_generation[x][y] = COLOR_ALIVE
                elif c == COLOR_DEAD:
                    if (n == 3):  # Rule number 4.
                        next_generation[x][y] = COLOR_ALIVE
                    else:
                        next_generation[x][y] = COLOR_DEAD
#Problem: first counting, then next iteration.

    # Defines button and mouse clicks.
    def handle_events(self):
        for event in pygame.event.get():
            # Turns the mouse position into a position in the grid.
            posn = pygame.mouse.get_pos()
            x = int(posn[0] / CELL_SIZE)
            y = int(posn[1] / CELL_SIZE)
            # Pressing quit --> quit the game.
            if event.type == pygame.QUIT:
                self.game_over = True
            # Pressing the left mouse button to activate or deactivate a cell.
            if event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    if next_generation[x][y] == COLOR_DEAD:
                        self.activate_living_cell(x, y)
                    else:
                        self.deactivate_living_cell(x, y)
            # Keeping the right mouse button pressed activates drawing mode.
            if event.type == pygame.MOUSEMOTION and event.buttons[2]:
                self.activate_living_cell(x, y)

            # Define the keyboard key presses for q, space, a, s, r.
            if event.type == pygame.KEYDOWN:
                # Quit the game.
                if event.unicode == 'q':
                    self.game_over = True
                    print("q")
                # Next iteration - manually.
                elif event.key == pygame.K_SPACE:
                    self.create_next_gen()
                    print("keypress")
                # Next iteration - automated.
                elif event.unicode == 'a':  # a to automate the iterations.
                    self.next_iteration = True
                    print("a")
                # Stop the automated iterations.
                elif event.unicode == 's':
                    self.next_iteration = False
                    print("s")
                # Empty the grid.
                elif event.unicode == 'r':
                    self.next_iteration = False
                    self.init_gen(next_generation, COLOR_DEAD)
                    print("r")

    # Runs the game loop
    def run(self):
        while not self.game_over:
            self.handle_events()
            if self.next_iteration:
                self.create_next_gen()
            self.update_gen()
            pygame.display.flip()
            self.fps_clock.tick(FPS_MAX)
            self.root.update()

if __name__ == "__main__":
    GAME = GameOfLife()
    GAME.run()

解决方案

You've to deep copy next_generation to current_generation. But

current_generation = list(next_generation)

doesn't do what you expect it to do, since the elements of next_generation are a list, too.

To deep copy a list of where each element is a list of numbers (objects won't be copied) you've to:

current_generation = [list(e) for e in next_generation]

or

current_generation = [[i for i in j] for j in next_generation]

or

current_generation = [e[:] for e in next_generation]

Since there is a nested loop in the method GameOfLife.update_gen, the issue can be solved by an simple assignment, too:

class GameOfLife:

    # [...]

    # Updating the cells in the current generation.
    def update_gen(self):
        global current_generation
        for y in range(Y_CELLS):
            for x in range(X_CELLS):
                c = next_generation[x][y]
                self.draw_cell(x, y, c)
                current_generation[x][y] = next_generation[x][y] # assign element by element


There is a further issue, when the animation is running and the game is manipulated by the mouse. When the mouse is pressed, then next_generation is changed by either .activate_living_cell or .deactivate_living_cell.
But after that next_generation is recalculated by the data in current_generation.

while not self.game_over:
     self.handle_events()       # change "next_generation" by click
     if self.next_iteration:
         self.create_next_gen() # compute "next_generation" from "current_generation"
     self.update_gen()          # copy "current_generation" from "next_generation"

The issue can be solved with ease. Consider that when the game is manipulated by the mouse, the content of current_generation and next_generation is equal.

Either change current_generation instead of next_generation or update current_generation after the manipulation:

def handle_events(self):
    for event in pygame.event.get():

        # [...]        

        # Pressing the left mouse button to activate or deactivate a cell.
        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                if next_generation[x][y] == COLOR_DEAD:
                    self.activate_living_cell(x, y)
                else:
                   self.deactivate_living_cell(x, y)
                self.update_gen() # <----------------------------

相关文章