直接看效果:
背景:
一个基于Tkinter的“生命游戏”(Game of Life),这是一个由数学家约翰·康威提出的细胞自动机模型。生命游戏的基本规则是,细胞在一个二维网格中根据周围邻居的状态进行繁殖和死亡。每个细胞可以是“活”的(1)或“死”的(0),并且细胞的状态在每个时间步长中会根据邻居的数量进行更新。
代码首先定义了一个Lifes
类,该类负责管理游戏的状态,包括细胞的布局、历史记录和游戏的运行状态。类中包含多个方法,例如reset_life
用于随机初始化细胞状态,reproduce
用于根据生命游戏的规则更新细胞状态,is_stable
用于检查游戏是否进入稳定状态。
在图形界面部分,使用Tkinter创建了一个窗口,并在窗口中绘制了一个36x36的网格,表示细胞的状态。用户可以通过按钮控制游戏的开始、暂停、随机初始化和清空细胞状态。每当游戏运行时,细胞的状态会根据规则进行更新,并在界面上实时显示。
当细胞状态达到稳定状态时,程序会弹出消息框提示用户,告知游戏的当前状态。这种可视化的生命游戏不仅展示了细胞自动机的基本原理,还为用户提供了一个互动的体验,帮助他们理解复杂系统的动态变化。
直接上代码:
import tkinter as tk
import random
import tkinter.messagebox as msgbox
class Lifes:
def __init__(self, rows=38, cols=38):
self.row = rows
self.col = cols
self.items = [[0] * self.col for _ in range(self.row)]
self.histroy = []
self.histroySize = 30
self.running = False
self.runningSpeed = 100
def reset_life(self, rate=0.1):
self.histroy = []
for i in range(self.row):
for j in range(self.col):
rnd = random.random()
if rnd > 1 - rate:
self.items[i][j] = 1
def reproduce(self):
new = [[0] * self.col for _ in range(self.row)]
self.add_history()
if len(self.histroy) > self.histroySize:
self.histroy.pop(0)
for i in range(self.row):
for j in range(self.col):
if i * j == 0 or i == self.row - 1 or j == self.col - 1:
new[i][j] = 0
else:
lifes = 0
for m in range(i - 1, i + 2):
for n in range(j - 1, j + 2):
if m == i and n == j:
continue
lifes += self.items[m][n]
if self.items[i][j]:
if lifes == 2 or lifes == 3:
new[i][j] = 1
else:
new[i][j] = 0
else:
if lifes == 3:
new[i][j] = 1
for idx, narray in enumerate(new):
self.items[idx] = narray
def is_stable(self):
if len(self.histroy) < self.histroySize:
return False
arr = []
for i in self.histroy:
if i not in arr:
arr.append(i)
if len(arr) < 10:
return True
def add_history(self, Items=None):
arr = []
if Items is None:
Items = self.items[:]
for item in Items:
b = 0
for i, n in enumerate(item[::-1]):
b += n * 2 ** i
arr.append(b)
self.histroy.append(arr)
def drawLifes():
R, XY = 8, [50 + i * 20 for i in range(36)]
if Life.running:
for i, x in enumerate(XY):
for j, y in enumerate(XY):
if Life.items[i + 1][j + 1]:
tv.itemconfig(rect[i][j], fill='green', outline='green')
else:
tv.itemconfig(rect[i][j], fill='lightgray', outline='lightgray')
tv.update()
Life.reproduce()
if Life.is_stable():
Life.running = False
if sum(sum(Life.items, [])):
msgbox.showinfo('Message', '生命繁殖与湮灭进入稳定状态!!!')
else:
msgbox.showinfo('Message', '生命已全部湮灭,进入死亡状态!!!')
win.after(Life.runningSpeed, drawLifes)
def drawCanvas():
global tv, rect
tv = tk.Canvas(win, width=win.winfo_width(), height=win.winfo_height())
tv.pack(side="top")
for i in range(36):
coord = 40, 40, 760, i * 20 + 40
tv.create_rectangle(coord)
coord = 40, 40, i * 20 + 40, 760
tv.create_rectangle(coord)
rect = [[0] * 36 for _ in range(36)]
for i in range(36):
for j in range(36):
rect[i][j] = tv.create_rectangle(50 + i * 20 - 8, 50 + j * 20 - 8, 50 + i * 20 + 8, 50 + j * 20 + 8, tags=('imgButton1'))
tv.itemconfig(rect[i][j], fill='lightgray', outline='lightgray')
def start_life():
Life.running = True
drawLifes()
def pause_game():
Life.running = False
def random_life():
Life.reset_life(rate=0.1)
drawLifes()
def clear_life():
Life.items = [[0] * Life.col for _ in range(Life.row)]
drawLifes()
def quit():
win.destroy()
if __name__ == '__main__':
win = tk.Tk()
X, Y = win.maxsize()
W, H = 1024, 800
winPos = f'{W}x{H}+{0}+{(Y - H) // 2}'
win.geometry(winPos)
win.resizable(True, True)
win.title('生命游戏')
win.update()
drawCanvas()
Life = Lifes()
tLabel = tk.Label(win, width=30, height=20, background='lightgray')
tLabel.place(x=780, y=38)
tLabel.config(text='游戏规则:细胞根据周围细胞的状态进行繁殖与死亡。')
tLabel.config(justify=tk.LEFT, anchor="nw", borderwidth=10, wraplength=210)
bX, bY, dY = 835, 458, 50
tButton0 = tk.Button(win, text='开始', command=start_life)
tButton0.place(x=bX, y=bY + dY * 0, width=120, height=40)
tButton1 = tk.Button(win, text='暂停', command=pause_game)
tButton1.place(x=bX, y=bY + dY * 1, width=120, height=40)
tButton2 = tk.Button(win, text='随机', command=random_life)
tButton2.place(x=bX, y=bY + dY * 2, width=120, height=40)
tButton3 = tk.Button(win, text='清空', command=clear_life)
tButton3.place(x=bX, y=bY + dY * 3, width=120, height=40)
win.protocol("WM_DELETE_WINDOW", quit)
win.mainloop()