【问题标题】:Error with a cellular-automata simulation in PythonPython 中的元胞自动机模拟错误
【发布时间】:2020-07-26 15:53:54
【问题描述】:

我正在尝试使用 Python 及其库“pygame”构建著名的 Conway“生命游戏”。

这是模拟的维基百科页面:https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life

代码:https://pastebin.com/kKGvshVK

(我也将其发布在问题的末尾,因为该网站告诉我必须输入一些代码)

我创建了一个长代码,应该按顺序执行以下操作:

  • 初始化pygame库和窗口;
  • 游戏基于二维网格,由大小相同的单元组成;
  • 创建一个包含每个单元格的属性和特征的“单元格”类; Fundamental 是“alive”属性,决定单元格是活着的(所以在屏幕上是黑色的)还是死的(白色的);
  • 创建一个函数checkAlive,用于检查单元格列表中的一个单元格是否处于活动状态;
  • 创建第二个函数neighbour,在其中使用checkAlive 输出邻居的数量(使用摩尔邻域理论);
  • while 循环中的单元格初始化;
  • 带有事件处理程序的主循环和应该按照游戏规则更新屏幕的算法。

现在,一些技术问题。我创建了确定邻居数量的函数,因为我(肯定)不了解应该帮助我制作这个游戏的简单算法(摩尔算法)。所以事情是,我从头开始制作这个函数,它很长,在它的开始,我做了多个异常,以便考虑何时分析单元格以确定它放置在屏幕边框或角落等。 如果这段代码有点乱,请见谅。

现在,问题是,我想我已经成功初始化了单元格,但是,单元格 s 没有更新,也没有错误。我认为在主循环内部发生了阻止流程的事情,因为应该打印“算法成功”的打印功能没有出现,所以有一个我不明白的错误。就是这样,谢谢您的任何回答!

#Conway Game of Life

import pygame
import random


#PYGAME INITIALIZATION
success, failure = pygame.init()

screen_width = 800
screen_height = 600

screen = pygame.display.set_mode((screen_width, screen_height)) #Init the screen
time = pygame.time.Clock() #Time from startup
FPS = 5

#Screen Area = 480000 px (width * height)
#Area of a cell = 100px --> 4800 Cell

BLACK = (0, 0, 0)#Live cell
WHITE = (255, 255, 255)#dead cell


class Cell:
    """ x: x coordinate
        y: y coordinate
        size: width and height (same)
        alive: int (boolean, 0 o 1), to track the status of a cell (live or dead), at the startup is random
    """
    def __init__(self, x, y, alive):
        self.x = x
        self.y = y
        self.size = 10 #it's a square
        self.alive = alive
        if self.alive == 1:
            self.color = BLACK
        elif self.alive == 0:
            self.color = WHITE

#Function needed in the next function ------------------------------------------------
def checkAlive(cell, cellArray, curr_x, curr_y, counter):
    """ Check wheter the current cell near the original cell is alive. If it is alive it adds 1 to the counter
        cell: instance of the original cell
        cellArray: cell list with all the initialized cells
        curr_x: x coordinate of the cell which will be examined
        curr_y: y coordinate of the cell which will be examined
        counter: variable that is updated whenever a cell near to original has the "alive" attribute == 1
    """

    for current_cell in cellArray:
        if (current_cell.x == curr_x and current_cell.y == curr_y):
            if (current_cell.alive == 1):
                counter += 1


#Function to find the neighbours of a cell ---------------------------------------------------

def neighbour(cells, cell):
    """Give as output the number of neighbours of a cell (only neighbours with the alive attribute = 1)
        cells: List containing all the instances of the initialized cells
        cell: The single instance of cell which will be examined to determine the number of live neighbours
        return: number of live neighbours
    """
    num_neighbours = 0 #Number of near live cells(Moore neighbourhood)
    x = cell.x
    y = cell.y

    #List of exceptions before the main algorithm
    #Upper-left corner (x = 0, y = 0) !*!*!*!*!*!*!*!*!**!*!*!*!*!*!*!*
    if (x == 0 and y == 0):
        #Cell on the right -----------
        current_x = 1
        current_y = 0

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current ----------------------------------------
        current_x = 1
        current_y = 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below original cell
        current_x = 0
        current_y = 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours

    #Upper-right corner (x = window, y = 0)!*!*!*!*!**!*!*!*!*!*!*!*!*!*!*!*!*!*!**!*!*!*!*!*!*!*!
    elif (x == screen_width - cell.size and y == 0):
        #Cell below -------------------------------------
        current_x = screen_width - cell.size
        current_y = 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current -----------------------------------
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of original
        current_y = 0

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours

    #Lower-left corner (x = 0, y = window) !*!*!*!**!*!!*!**!*!!**!*!*!*!*!*
    elif(x == 0 and y == (screen_height - cell.size)):

        #Cell over original ----------------------
        current_x = 0
        current_y = (screen_height - cell.size) - 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current ------------------------------------------
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current ---------------------------------------------
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours



    #Lower right corner !*!*!*!*!*!!*!*!*!*!*!*!**!!*!*!*
    elif (x == (screen_width - cell.size) and y == (screen_height - cell.size)):

        #Cell to the left of original ------------------------------------------------
        current_x = (screen_width - cell.size) - 1
        current_y = screen_height - cell.size

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current -------------------------------------------------------
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours


    #If the cell is in the first row (y = 0) (2 corners excluded) !*!*!*!*!*!!*!!*!*!*!*!
    elif (y == 0 and (x != 0 and x != (screen_width - cell.size))):
        #Cell to the right of original
        current_x = x + 1
        current_y = 0

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below original
        current_x = x

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of original
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours


    #If the cell is in the last row (y = screen_height) 2 corners excluded !*!*!*!*!*!*!*!!*!*
    elif (y == (screen_height - cell.size) and (x != 0 and x != (screen_width - cell.size))):
        #Cell to the left of original
        current_x = x - 1
        current_y = y

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Return the number of neighbours
        return num_neighbours


    #If the cell is in the first column (2 corners excluded) !*!*!*!*!*!*!*!*!*!*!*!*
    elif (x == 0 and (y != 0 and y != (screen_height - cell.size))):
        #Cell on top of original
        current_x = x
        current_y = y - 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)


        return num_neighbours


    #If the cell is in the last column (x = screen width) !*!*!*!*!*!*!*!!**!!*
    elif (x == (screen_width - cell.size) and (y != 0 and y != (screen_height - cell.size))):
        #Cell below original
        current_x = x
        current_y = y + 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of current
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        return num_neighbours


    #GENERAL RULE
    else:
        #8 Neighbours
        #Cell on top of original
        current_x = x
        current_y = y - 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the right of original
        current_x += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell below current
        current_y += 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell to the left of current
        current_x -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        #Cell on top of current
        current_y -= 1

        checkAlive(cell, cells, current_x, current_y, num_neighbours)

        return num_neighbours




#CELL INITIALIZATION
cell_array = []
#Useful variable in the for loop
x = 0
y = 0
init = False #Become true when Initialization is completed


#Initialization
while not init:

    is_alive = random.choices([0,1], weights = (95, 5), k=1)[0]#Randomly spawn cells with probability (Dead 95%, Alive 5 %)
    cell = Cell(x, y, is_alive)#Single object
    x += cell.size
    cell_array.append(cell)
    if x == screen_width: #End of a row
        x = 0
        y += cell.size
    if y == screen_height:#Last row
        init = True


#DRAWING CELLS
for cl in cell_array:
    pygame.draw.rect(screen, cl.color, pygame.Rect(cl.x, cl.y, cl.size, cl.size))#Draw any single cell

pygame.display.flip() #To update the screen

#Debug
print("Initialization Completed.")


done = False #Check whether the program should run

#Main loop
while not done:
    #FPS
    time.tick(FPS)

    #EVENT HANDLER
    for event in pygame.event.get():
        if event.type == pygame.QUIT: #Exit button
            print("Quitting.")
            done = True



    #SIMULATION --------------------------------------------------------------------

    #Run the algorithm of the game and update the screen (Moore algorithm)
    for cell in cell_array:
        if neighbour(cell_array, cell) in (2, 3): #2 or 3 live neighbours (survive)
            cell.alive = 1
        elif neighbour(cell_array, cell) < 2: #Few than 2 live neighbours (dies)
            cell.alive = 0
        elif neighbour(cell_array, cell) > 3: #More than 3 live neighbours (dies)
            cell.alive = 0
        elif ((cell.alive == 0) and (neighbour(cell_array, cell) == 3)): #Dead cell with 3 live neigh (live)
            cell.alive == 1

    #Debug
    print("Algorithm succesful.")

    #DRAWING CELLS
    for cl in cell_array:
        pygame.draw.rect(screen, cl.color, pygame.Rect(cl.x, cl.y, cl.size, cl.size))
    #Debug
    print("Cell loaded to the screen")

    pygame.display.flip() #To update the screen

【问题讨论】:

  • 请创建一个minimal reproducible example。不要粘贴所有您的代码。
  • 您可能会觉得这很有帮助:ericlippert.com/2014/03/05/how-to-debug-small-programs
  • 我没有问任何具体的问题,因为我不知道错误在哪里,我会尝试做一个最小的例子,但我不知道我是否可以做到。
  • 我认为,如果您将单元格支架(或四叉树等)的二维数组作为查找表,您的代码会更省时。因此,查找任何给定单元的邻居会更容易/更快。您仍然可以继续使用基于单元列表的处理(这对于人口稀少的地区非常有用),但它会将您的 neighbour() 函数减少到大约 10 行,并将 checkAlive() 减少到单个 1 行查找。
  • @Kingsley 你的意思是按 X 和 Y 坐标排序的二维单元格数组吗?

标签: python pygame simulation cellular-automata


【解决方案1】:

这里有很多错误。您应该逐步检查这些功能是否正常工作。几个例子:

  1. checkAlive 方法可能应该检查一些东西并返回原始单元格附近的当前单元格是否像你写的那样活着。它不返回任何东西。相反,它会更改未返回的 counter 的值
  2. 可能您希望counter 变量在checkAlive 函数中与neighbour 函数中的变量num_neighbours 相同 - 不,它们是不同的原语。
  3. 当您更改alive 属性时 - 您没有将颜色设置为正确的颜色,因此alive 状态会更改,但颜色不会更改。你应该在你设置颜色的地方创建一个 setter。

当您完成更改这些问题后,您可能会通过代码,我们会认为还有什么问题。

【讨论】:

  • 是的,我用 python 调试器分析了代码,现在很多事情都清楚了。我更改了 checkAlive 函数,现在它返回了一些添加到 num_neighbours 计数器的值,并且当我尝试使用 pygame 库进行绘制时也存在问题(我认为我没有使用正确的函数来更新屏幕)。当我修复部分代码时,我应该在哪里发布?
  • 您可以在此处发表评论或编辑问题并将新的更新版本与旧版本一起发布。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-05-04
  • 2013-06-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-25
  • 2011-04-25
相关资源
最近更新 更多