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
该游戏操作规则:
按空格键暂停/启动。
左键单击可添加活动单元格,右键单击可删除单元格。
先给出效果图:
代码如下:
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()