pygame + numpy实现康威生命游戏

pygame + numpy实现康威生命游戏

康威生命游戏介绍

康威生命游戏(Conway's Game of Life)是一种零玩家游戏,属于元胞自动机的一种。它由数学家约翰·康威(John Horton Conway)在1970年发明。生命游戏在一个无限的二维网格上进行,每个格子代表一个细胞,每个细胞有两种状态:存活或死亡。

★细胞的下一个状态由其周围的八个邻居细胞的状态按以下规则决定(康威生命游戏的规则是):

如果一个存活的细胞周围有2个或3个存活的邻居,那么该细胞在下一代中继续存活。

如果一个死亡的细胞周围正好有3个存活的邻居,那么该细胞在下一代中变为存活状态。

在所有其他情况下,一个细胞要么保持死亡状态,要么变为死亡状态。

★随着游戏的进行,细胞群体将经历多代的演化,可能会出现以下几种情况:

稳定状态:细胞群体停止了变化,达到了稳定状态。也就是说,所有的细胞都遵循规则1和规则2,没有细胞死亡或新生。

摆动(振荡)状态:细胞群体在两种或多种状态之间循环变化。也就是说,每过几代,细胞群体会回到原来的状态。

移动状态:细胞群体作为一个整体在网格中移动。也就是说,每过一代,细胞群体的位置会改变,但其形状保持不变。

灭绝状态,因为所有的细胞都死亡了。在这个状态下,如果没有新的细胞被添加到游戏中,网格将一直保持空白,因为已经没有细胞可以复活或新生了。

混乱状态:细胞群体在持续变化,并没有显示出稳定、摆动或移动的模式。

需要注意的是,康威生命游戏是一个确定性的游戏,也就是说,给定一个初始状态,下一代的状态是确定的,不依赖于随机因素。换句话说,如果你从同一个初始配置开始,每次运行游戏都会得到相同的结果。因此,以上所描述的所有可能的情况都是可以预测的。尽管游戏是确定性的,但由于某些模式可能非常复杂,预测它们的长期行为可能在实践中是非常困难的,尤其是对于大型和复杂的初始配置。

康威生命游戏的代码

下面给出pygame + numpy实现康威生命游戏的代码

关于pygame安装使用可参见 https://blog.csdn.net/cnds123/article/details/119514520

关于numpy安装使用可参见https://blog.csdn.net/cnds123/article/details/135844660

该游戏操作规则:

按空格键暂停/启动。

左键单击可添加活动单元格,右键单击可删除单元格。

先给出效果图:

pygame + numpy实现康威生命游戏_第1张图片

代码如下:

import pygame
import numpy as np

# 初始化Pygame
pygame.init()

# 设置窗口大小
width, height = 800, 600
screen = pygame.display.set_mode((width, height))

# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)

# 设置网格大小
rows, cols = 60, 80
cell_size = width // cols

# 创建网格数组
grid = np.zeros((rows, cols), dtype=int)

# 初始化一些细胞为存活状态
grid[28, 37] = 1
grid[28, 38] = 1
grid[29, 39] = 1
grid[30, 40] = 1
grid[31, 41] = 1
grid[32, 39] = 1
grid[32, 40] = 1
grid[32, 41] = 1

# 计算细胞的邻居数量
def count_neighbors(grid, row, col):
    return sum([
        grid[(row - 1) % rows, (col - 1) % cols],
        grid[(row - 1) % rows, col],
        grid[(row - 1) % rows, (col + 1) % cols],
        grid[row, (col - 1) % cols],
        grid[row, (col + 1) % cols],
        grid[(row + 1) % rows, (col - 1) % cols],
        grid[(row + 1) % rows, col],
        grid[(row + 1) % rows, (col + 1) % cols]
    ])

# 游戏循环
running = True
pause = True  # 开始时暂停游戏
while running:
    screen.fill(WHITE)

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            col, row = pygame.mouse.get_pos()
            col = col // cell_size
            row = row // cell_size
            if event.button == 1:  # 左键
                grid[row, col] = 1
            elif event.button == 3:  # 右键
                grid[row, col] = 0
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                pause = not pause  # 按空格键暂停或开始游戏

    # 绘制网格线
    for row in range(rows):
        for col in range(cols):
            rect = pygame.Rect(col * cell_size, row * cell_size, cell_size, cell_size)
            if grid[row, col] == 1:
                pygame.draw.rect(screen, BLACK, rect)
            pygame.draw.rect(screen, GRAY, rect, 1)  # 绘制网格线

    # 更新网格
    if not pause:
        new_grid = grid.copy()
        for row in range(rows):
            for col in range(cols):
                neighbors = count_neighbors(grid, row, col)
                if grid[row, col] == 1 and (neighbors < 2 or neighbors > 3):
                    new_grid[row, col] = 0
                elif grid[row, col] == 0 and neighbors == 3:
                    new_grid[row, col] = 1
        grid = new_grid

    pygame.display.flip()
    pygame.time.delay(100)

# 退出Pygame
pygame.quit()

代码改进

下面修改这个代码,为这个程序帮助,添加按F1键给出提示游戏帮助信息对话框。

这里使用tkinter创建一个弹出的对话框来显示帮助信息。需要注意,pygame和tkinter两者都有各自的事件循环,直接在pygame游戏中调用tkinter可能会导致问题。一个解决方案是使用多线程,将tkinter对话框放在一个单独的线程中。

首先,添加Python标准库中的threading库,并在初始化部分创建一个用于存储对话框状态的变量:

import threading
...
dialog_open = False
#接着,创建一个新的函数用于显示对话框:
def show_help_dialog():
    global dialog_open
    dialog_open = True
    import tkinter as tk
    from tkinter import messagebox
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    messagebox.showinfo("游戏帮助", 
                        "- 单击鼠标左键放置细胞\n"
                        "- 单击鼠标右键移除细胞\n"
                        "- 按空格键开始/暂停游戏\n"
                        "- 按F1键显示此帮助")
    root.destroy()
dialog_open = False

最后,修改处理F1键按下事件的代码,使其在一个新的线程中打开对话框。即:

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                pause = not pause  # 按空格键暂停或开始游戏

部分,改为:

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                pause = not pause  # 按空格键暂停或开始游戏
            if event.key == pygame.K_F1:  # 按下F1键,弹出提示对话框
                if not dialog_open:
                    threading.Thread(target=show_help_dialog).start()

为照顾新手,下面给出改进后的完整代码

import pygame
import numpy as np
import threading  #

dialog_open = False #存储对话框状态的变量
#接着,创建一个新的函数用于显示对话框
def show_help_dialog():
    global dialog_open
    dialog_open = True
    import tkinter as tk
    from tkinter import messagebox
    root = tk.Tk()
    root.withdraw()  # 隐藏主窗口
    messagebox.showinfo("游戏帮助", 
                        "- 单击鼠标左键放置细胞\n"
                        "- 单击鼠标右键移除细胞\n"
                        "- 按空格键开始/暂停游戏\n"
                        "- 按F1键显示此帮助")
    root.destroy()
    dialog_open = False

# 初始化Pygame
pygame.init()

# 设置窗口大小
width, height = 800, 600
screen = pygame.display.set_mode((width, height))

# 设置颜色
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
GRAY = (200, 200, 200)

# 设置网格大小
rows, cols = 60, 80
cell_size = width // cols

# 创建网格数组
grid = np.zeros((rows, cols), dtype=int)

# 初始化一些细胞为存活状态
grid[28, 37] = 1
grid[28, 38] = 1
grid[29, 39] = 1
grid[30, 40] = 1
grid[31, 41] = 1
grid[32, 39] = 1
grid[32, 40] = 1
grid[32, 41] = 1

# 计算细胞的邻居数量
def count_neighbors(grid, row, col):
    return sum([
        grid[(row - 1) % rows, (col - 1) % cols],
        grid[(row - 1) % rows, col],
        grid[(row - 1) % rows, (col + 1) % cols],
        grid[row, (col - 1) % cols],
        grid[row, (col + 1) % cols],
        grid[(row + 1) % rows, (col - 1) % cols],
        grid[(row + 1) % rows, col],
        grid[(row + 1) % rows, (col + 1) % cols]
    ])

# 游戏循环
running = True
pause = True  # 开始时暂停游戏
while running:
    screen.fill(WHITE)

    # 处理事件
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
        if event.type == pygame.MOUSEBUTTONDOWN:
            col, row = pygame.mouse.get_pos()
            col = col // cell_size
            row = row // cell_size
            if event.button == 1:  # 左键
                grid[row, col] = 1
            elif event.button == 3:  # 右键
                grid[row, col] = 0
        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_SPACE:
                pause = not pause  # 按空格键暂停或开始游戏
            if event.key == pygame.K_F1:  # 按下F1键,弹出提示对话框
                if not dialog_open:
                    threading.Thread(target=show_help_dialog).start()

    # 绘制网格线
    for row in range(rows):
        for col in range(cols):
            rect = pygame.Rect(col * cell_size, row * cell_size, cell_size, cell_size)
            if grid[row, col] == 1:
                pygame.draw.rect(screen, BLACK, rect)
            pygame.draw.rect(screen, GRAY, rect, 1)  # 绘制网格线

    # 更新网格
    if not pause:
        new_grid = grid.copy()
        for row in range(rows):
            for col in range(cols):
                neighbors = count_neighbors(grid, row, col)
                if grid[row, col] == 1 and (neighbors < 2 or neighbors > 3):
                    new_grid[row, col] = 0
                elif grid[row, col] == 0 and neighbors == 3:
                    new_grid[row, col] = 1
        grid = new_grid

    pygame.display.flip()
    pygame.time.delay(100)

# 退出Pygame
pygame.quit()

你可能感兴趣的:(Python学习,编程资料,pygame,numpy)