Python 生命游戏(tkinter版)

简介: Python 生命游戏(tkinter版)

生命游戏(Game of Life)


由剑桥大学约翰·何顿·康威设计的计算机程序。美国趣味数学大师马丁·加德纳(Martin Gardner,1914-2010)通过《科学美国人》杂志,将康威的生命游戏介绍给学术界之外的广大渎者,一时吸引了各行各业一大批人的兴趣,这时细胞自动机课题才吸引了科学家的注意。




游戏概述


用一个二维表格表示“生存空间”,空间的每个方格中都可放置一个生命细胞,每个生命细胞只有两种状态:“生”或“死”。用绿色方格表示该细胞为“生”,空格(白色)表示该细胞为“死”。或者说方格网中绿色部分表示某个时候某种“生命”的分布图。生命游戏想要模拟的是:随着时间的流逝,这个分布图将如何一代一代地变化。死亡太沉重,我想称它为“湮灭”状态。




生存定律


生存空间的每个方格都存在一个细胞,它的周边紧邻的8个方格上的称为邻居细胞。


(1)当前细胞为湮灭状态时,当周围有3个存活细胞时,则迭代后该细胞变成存活状态(模拟繁殖)。

(2)当前细胞为存活状态时,当周围的邻居细胞少于2个存活时,该细胞变成湮灭状态(数量稀少)。

(3)当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成湮灭状态(数量过多)。

(4)当前细胞为存活状态时,当周围有2个或3个存活细胞时,该细胞保持原样。


简单来说,活细胞Cell看作是‘1’,死Cell看作‘0’,8个邻居的累加和Sum决定了下一轮的状态:


“繁殖”:死Cell=0,若Sum=3,则Cell=1。

“稀少”:活Cell=1,若Sum<2,则Cell=0。

“过多”:活Cell=1,若Sum>3,则Cell=0。

“正常”:活Cell=1,若Sum=2或3,则Cell=1。


生存空间中生命的繁殖和湮灭,如下图所示:

image.gif



图形结构


在游戏进行中,杂乱无序的细胞会逐渐演化出各种精致、有形的图形结构;这些结构往往有很好的对称性,而且每一代都在变化形状。一些形状一经锁定就不会逐代变化。有时,一些已经成形的结构会因为一些无序细胞的“入侵”而被破坏。但是形状和秩序经常能从杂乱中产生出来。


通常会有以下四种状态:


不动点(fixed points):变化终结于恒定图像

交替态(alternation):图像出现周期性变化

随机态(randomness):图像变化近乎随机

复杂态(complexity):图像存在某种复杂规律


常见的不动结构:

aefc5e1438e3427dbc0a6f618db2973a.png


周期变化的结构:

f0c49f1081a740bca2af30a8c3c388bd.gif


逐步趋向湮灭的结构:

407a6dfa7b04459eb3204dc0e56f9fc8.gif


由一根10个连续细胞演化出来的周期结构:

6fa427412c8b49dbb6357bcdfa72c06a.gif



动态变化后全部湮灭的结构:


e14012459496405ab79d9a6ac0afdddd.gif



移动的飞船:周期变化且逐渐向右平移

飞船到了边界变成向对角线移动的“滑翔者”,滑翔者到了边界成为不动的方块


284444bf1e424aada07300930840588b.gif


更多有趣的图形结构有待发现,用代码来辅助这项工作还是比较方便的.....


代码实现

tkinter库实现了软件界面,画布、按钮、标签等控件都是配角,全是为表现生命繁殖演化的核心代码类方法 Lifes.reproduce() 作帮手的,源代码lifegame.pyw如下:

from tkinter import messagebox as msgbox
import tkinter as tk
import webbrowser
import random
class Lifes:
    def __init__(self, rows=36, cols=36):
        self.row = rows+2
        self.col = cols+2
        self.items = [[0]*self.col for _ in range(self.row)]
        self.histroy = []
        self.histroySize = 30
        self.running = False
        self.runningSpeed = 100
    def rndinit(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_histroy()
        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_histroy(self, Items=None):
        arr = []
        if Items==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 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)
    coord = 38, 38, 760, 760
    tv.create_rectangle(coord,width=2)
    coord = 39, 39, 760, 760
    tv.create_rectangle(coord,width=2)
    coord = 38, 38, 762, 762
    tv.create_rectangle(coord,width=2)
    R,XY = 8,[50+i*20 for i in range(36)]
    rect = [[0]*36 for _ in range(36)]
    for i,x in enumerate(XY):
        for j,y in enumerate(XY):
            rect[i][j] = tv.create_rectangle(x-R,y-R,x+R,y+R,tags=('imgButton1'))
            tv.itemconfig(rect[i][j],fill='lightgray',outline='lightgray')
    tv.tag_bind('imgButton1','<Button-1>',on_Click)
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 StartLife():
    if sum(sum(Life.items,[])):
        Life.histroy = []
        Life.running = True
    else:
        msgbox.showinfo('Message','请点击小方块填入生命细胞,或者使用随机功能!')
def BreakLife():
    Life.running = not Life.running
    if Life.running:
        Life.histroy.clear()
        Life.add_histroy()
def RandomLife():
    Life.rndinit()
    Life.running = True
def ClearLife():
    Life.running = False
    Life.histroy = []
    Life.items = [[0]*38 for _ in range(38)]
    for x in range(36):
        for y in range(36):
            tv.itemconfig(rect[x][y],fill='lightgray',outline='lightgray')
def btnCommand(i):
    if   i==0: return StartLife
    elif i==1: return BreakLife
    elif i==2: return RandomLife
    elif i==3: return ClearLife
def on_Enter(event):
    tCanvas.itemconfig(tVisit, fill='magenta')
def on_Leave(event):
    tCanvas.itemconfig(tVisit, fill='blue')
def on_Release(event):
    url = 'https://blog.csdn.net/boysoft2002?type=blog'
    webbrowser.open(url, new=0, autoraise=True)
def on_Click(event):
    x,y = (event.x-40)//20,(event.y-40)//20
    if not Life.running:
        if Life.items[x+1][y+1]:
            tv.itemconfig(rect[x][y],fill='lightgray',outline='lightgray')
        else:
            tv.itemconfig(rect[x][y],fill='red',outline='red')
        Life.items[x+1][y+1] = not Life.items[x+1][y+1]
def on_Close():
    if msgbox.askokcancel("Quit","Do you want to quit?"):
        Life.running = False
        print(Copyright())
        win.destroy()
def Introduce():
    txt = '''【生命游戏】\n\n生存定律:
    (1)当前细胞为湮灭状态时,当周围有3个存活细胞时,则迭代后该细胞变成存活状态(模拟繁殖)。
    (2)当前细胞为存活状态时,当周围的邻居细胞少于2个存活时,该细胞变成湮灭状态(数量稀少)。
    (3)当前细胞为存活状态时,当周围有3个以上的存活细胞时,该细胞变成湮灭状态(数量过多)。
    (4)当前细胞为存活状态时,当周围有2个或3个存活细胞时,该细胞保持原样。'''
    return txt
def Copyright():
    return 'Lifes Game Ver1.0\nWritten by HannYang, 2022/08/01.'
if __name__ == '__main__':
    win = tk.Tk()
    X,Y = win.maxsize()
    W,H = 1024,800
    winPos = f'{W}x{H}+{(X-W)//2}+{(Y-H)//2}'
    win.geometry(winPos)
    win.resizable(False, False)
    win.title('生命游戏 Ver1.0')
    win.update()
    drawCanvas()
    Life = Lifes()
    drawLifes()
    tLabel = tk.Label(win, width=30, height=20, background='lightgray')
    tLabel.place(x=780, y=38)
    tLabel.config(text='\n\n\n'.join((Introduce(),Copyright())))
    tLabel.config(justify=tk.LEFT,anchor="nw",borderwidth=10,wraplength=210)
    tButton = [None]*4
    bX,bY,dY = 835, 458, 50
    txt = ['开始','暂停','随机','清空']
    for i in range(4):
        tButton[i] = tk.Button(win, text=txt[i], command=btnCommand(i))
        tButton[i].place(x=bX, y=bY+dY*i, width=120, height=40)     
    tCanvas = tk.Canvas(win, width=200, height=45)
    tCanvas.place(x=800,y=716)
    tVisit = tCanvas.create_text((88, 22), text=u"点此访问Hann's CSDN主页!")
    tCanvas.itemconfig(tVisit, fill='blue', tags=('btnText'))
    tCanvas.tag_bind('btnText','<Enter>',on_Enter)
    tCanvas.tag_bind('btnText','<Leave>',on_Leave)
    tCanvas.tag_bind('btnText','<ButtonRelease-1>',on_Release)
    win.protocol("WM_DELETE_WINDOW", on_Close)
    win.mainloop()



编译命令

D:\> pyinstaller -F lifegame.pyw --noconsole




运行界面


aa8b27152b7e454eaf3a2a6110878b06.png



使用简介

在生存空间里点击方格种植细胞(甚至可以画出你要表达的图形),然后点击开始;点下暂停键,则可以任意编辑生命图形,再点开始继续运行;点随机键则由软件随机生成细胞位置;清空键可以在任何时候清空生存空间,进入暂停编辑状态。



后续改进


Lifes类预留了histroy属性,后续可以增加回退功能;代码缺点是生存空间的行列被我写死了,以后版本中可以改进成任意定义行列数;另一个缺点是对稳定状态的判断比较简单,之后可以增加计算变化周期的功能。

优点就是可以任意编辑你的图形,最后以一张自己的网名画的图作收尾:


410bc801568c40c8bf6337326f5f6755.gif


目录
相关文章
|
8天前
|
数据可视化 开发者 Python
Python GUI开发:Tkinter与PyQt的实战应用与对比分析
【10月更文挑战第26天】本文介绍了Python中两种常用的GUI工具包——Tkinter和PyQt。Tkinter内置于Python标准库,适合初学者快速上手,提供基本的GUI组件和方法。PyQt基于Qt库,功能强大且灵活,适用于创建复杂的GUI应用程序。通过实战示例和对比分析,帮助开发者选择合适的工具包以满足项目需求。
40 7
|
1月前
|
IDE 开发工具 Python
Python扑克游戏编程---摸大点
Python扑克游戏编程---摸大点
|
2月前
|
前端开发 Python
python之【Tkinter模块】
python之【Tkinter模块】
36 5
|
2月前
|
Python
python编写下象棋游戏|4-14
python编写下象棋游戏|4-14
|
2月前
|
人工智能 算法 图形学
总有一个是你想要的分享40个Python游戏源代码
这是一系列基于Python开发的游戏项目集合,包括中国象棋、麻将、足球、坦克大战、扑克等多种类型游戏,运用了Pygame等库实现图形界面与AI算法。此外还包含迷宫、数独、推箱子等益智游戏及经典游戏如《仙剑奇侠传二战棋版》和《星露谷物语》的Python版本,适合编程学习与娱乐。
94 11
|
1月前
|
数据采集 前端开发 Python
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
Python pygame 实现游戏 彩色 五子棋 详细注释 附源码 单机版
63 0
|
2月前
|
消息中间件 数据采集 数据库
庆祝吧!Python IPC让进程间的合作,比团队游戏还默契
【9月更文挑战第7天】在这个数字化时代,软件系统日益复杂,单进程已难以高效处理海量数据。Python IPC(进程间通信)技术应运而生,使多进程协作如同训练有素的电竞战队般默契。通过`multiprocessing`模块中的Pipe等功能,进程间可以直接传递数据,无需依赖低效的文件共享或数据库读写。此外,Python IPC还提供了消息队列、共享内存和套接字等多种机制,适用于不同场景,使进程间的合作更加高效、精准。这一技术革新让开发者能轻松应对复杂挑战,构建更健壮的软件系统。
35 1
|
3月前
|
机器学习/深度学习 存储 定位技术
强化学习Agent系列(一)——PyGame游戏编程,Python 贪吃蛇制作实战教学
本文是关于使用Pygame库开发Python贪吃蛇游戏的实战教学,介绍了Pygame的基本使用、窗口初始化、事件处理、键盘控制移动、以及实现游戏逻辑和对象交互的方法。
|
3月前
|
机器学习/深度学习 人工智能 自然语言处理
【机器学习】python之人工智能应用篇--游戏生成技术
游戏生成技术,特别是生成式人工智能(Generative Artificial Intelligence, 简称Generative AI),正逐步革新游戏开发的多个层面,从内容创作到体验设计。这些技术主要利用机器学习、深度学习以及程序化内容生成(Procedural Content Generation, PCG)来自动创造游戏内的各种元素,显著提高了开发效率、丰富了游戏内容并增强了玩家体验。以下是生成式AI在游戏开发中的几个关键应用场景概述
52 2
|
3月前
|
Python
python tkinter 实现简易秒表计时器
python tkinter 实现简易秒表计时器
100 1

热门文章

最新文章