Python实现康威生命游戏

一.游戏介绍

康威生命游戏 Conway's Game of Life,又称康威生命棋,是英国数学家约翰·何顿·康威 John Horton Conway在1970年发明的元胞自动机。可以用来模拟细胞的自然进化

比较经典的一版游戏规则概述如下:

1. 每个细胞的状态由该细胞及周围八个细胞上一次的状态所决定;
2. 如果一个细胞周围有3个细胞为生,则该细胞由死转生或保持生;
3. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;
4. 在其它情况下,该细胞由生转死或保持死

二.总体思路

总体内容包括游戏GUI界面的设计、游戏逻辑的具体实现及其他拓展功能的实现。主要涉及的技术方法:Python的基础语法、tkinter库的基本控件的使用及事件响应的处理、Python中类的定义和使用。

三.实现过程

1.创建GUI界面

游戏主界面为一块900x900像素的60x60方格的网格画布,每一个方格代表一个细胞位置,界面中还有四个功能按钮:开始、暂停、清空、随机,分别用于开始演化、暂停演化、清空界面中的所有细胞及以一定的概率随机生成初始细胞。除此之外界面中还有一个用于导入经典图形的组合框、两个用于调节参数(随机生成时的概率、演化频率)的滑块和一个显示信息和单步演化的菜单。

2.菜单实现

界面中有一个菜单,三个子菜单内容为:游戏规则、单步演化和关于游戏。单步演化通过取消self.root.after()函数实现,其他两个功能通过两个label实现。

3.经典图形导入功能

组合框列出了五个经典图形,选中某个图形时通过字典查找对应的键便可显示相应图形的初始界面,图形的具体显示采用涂点的方式。

4.游戏逻辑相关

游戏逻辑中,对于鼠标在画布上的单击事件,会将得到的鼠标相对于界面的坐标分别整除网格像素大小,这样就得到了对应于二维数组中的元素,通过鼠标单击翻转对应网格的状态。游戏界面的网格绘制使用self.canvas.create_rectangle()函数实现;开始演化和暂停演化通过设置运行标志位的值实现;清空细胞是将每个网格中的细胞设为死亡状态;随机生成细胞的实现是将每一个网格按照设置的概率涂黑;游戏运行时需要时刻更新每个方格细胞的状态,就需要统计每个网格周围8个(边界相应减少)格子中活细胞的数量,通过遍历的方式来实现统计;最后就是根据统计到的活细胞数量及游戏规则更新下一步的状态,即为一次演化。

5.参数设置及引用库

参数包括细胞状态、细胞颜色、随机概率、演化速度、网格数、网格边长、运行状态标志位和单步演化标志位等。

以上就是类及类之前的所有内容,最后在外部定义和调用相关函数就可以实现运行:

游戏界面如下:

四.完整代码

#@photon_123 
import tkinter as tk
import tkinter.ttk as ttk
import random

# 定义细胞的状态
DEAD = 0
ALIVE = 1

# 定义细胞的颜色
COLORS = {
    DEAD: "white",
    ALIVE: "black"
}

pran =0.1  #初始随机概率
speed = 1  #初始演化速度
class GameOfLife:
    def __init__(self, width, height, cell_size=20):
        self.width = width
        self.height = height
        self.cell_size = cell_size
        self.board = [[DEAD for _ in range(height)] for _ in range(width)]
        self.running = True   #运行状态标志位
        self.onestepp = False #单步演化标志位

####创建GUI界面##############################################################################################################################################################################################################################################
 
        self.root = tk.Tk()
        self.root.title("康威生命游戏")
        self.canvas = tk.Canvas(self.root, width=width*cell_size, height=height*cell_size)
        self.canvas.pack()
        self.canvas.bind("<Button-1>", self.handle_click)
        self.start_button = tk.Button(self.root, text="开始", fg='white',bg='#4CAF50',font=('黑体',self.cell_size),command=self.start)
        self.start_button.place(relheight=0.05,relwidth=0.16,relx=0.02,rely=0.9)
        self.pause_button = tk.Button(self.root, text="暂停", fg='white',bg='#FE5E08',font=('黑体',self.cell_size),command=self.pause)
        self.pause_button.place(relheight=0.05,relwidth=0.1,relx=0.18,rely=0.9)
        self.clear_button = tk.Button(self.root, text="清空", fg='white',bg='#555555',font=('黑体',self.cell_size),command=self.clear)
        self.clear_button.place(relheight=0.05,relwidth=0.1,relx=0.28,rely=0.9)
        self.random_button = tk.Button(self.root, text="随机",fg='white',bg='#5677FC',font=('黑体',self.cell_size), command=self.random)
        self.random_button.place(relheight=0.05,relwidth=0.1,relx=0.38,rely=0.9)
        var = tk.StringVar()
        ls = ['滑翔机','太空船','高斯帕滑翔机枪','蜂王梭','慨影']
        self.combobox = ttk.Combobox(self.root, textvariable=var, values=ls,font=('黑体',self.cell_size))
        self.combobox.place(relheight=0.05,relwidth=0.25,relx=0.7,rely=0.9)
        self.combobox.bind('<<ComboboxSelected>>',self.anli)

        var2 = tk.DoubleVar()
        def adjust1(event):
            global pran
            pran = var2.get()      
        self.scale = tk.Scale(self.root,from_=0.1,to=1,resolution=0.01,bg='#A9A9A9',orient=tk.HORIZONTAL,length=200,variable=var2)
        self.scale.bind('<ButtonRelease-1>',adjust1)
        self.scale.place(relx=0.66,rely=0.03)
        self.labe1 = tk.Label(self.root,text='随机概率',font=('黑体',15))
        self.labe1.place(relx=0.9,rely=0.03)

        var3 = tk.IntVar()
        def adjust2(event):
            global speed
            speed = var3.get()
        self.scale2 = tk.Scale(self.root,from_=1,to=100,resolution=1,bg='#A9A9A9',orient=tk.HORIZONTAL,length=200,variable=var3)
        self.scale2.bind('<ButtonRelease-1>',adjust2)
        self.scale2.place(relx=0.66,rely=0.1)
        self.labe2 = tk.Label(self.root,text='演化频率',font=('黑体',15))
        self.labe2.place(relx=0.9,rely=0.1)

####菜单相关#################################################################################################################################################################################################################################################

        def rule():
            rulewin = tk.Toplevel(self.root)
            rulewin.geometry('700x200')
            rulewin.title('游戏规则')
            lab = tk.Label(rulewin,text='''1. 每个细胞的状态由该细胞及周围八个细胞上一次的状态所决定;\n
2. 如果一个细胞周围有3个细胞为生,则该细胞由死转生或保持生;\n
3. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;\n
4. 在其它情况下,该细胞由生转死或保持死''',fg='black',font=('黑体',15))
            lab.pack()
        def about():
            abowin = tk.Toplevel(self.root)
            abowin.geometry('700x200')
            abowin.title('关于游戏')
            lab2 = tk.Label(abowin,text='''---一个不太聪明的Python小程序---\n
                -young 2023.6''',fg='black',font=('黑体',15))
            lab2.place(relx=0.25,rely=0.3)
        def onestep():
            self.onestepp = True
            self.running = True
            self.evolve()
        self.menu = tk.Menu(self.root,relief=tk.SUNKEN)
        menufile = tk.Menu(self.menu)
        self.menu.add_cascade(label='菜单',menu=menufile)
        menufile.add_command(label='游戏规则',command=rule)
        menufile.add_command(label='关于',command=about)
        menufile.add_command(label='运行一步',command=onestep)
        self.root.config(menu=self.menu)
    
############################################################################################################################################################################################################################################################


    def anli(self,event):
        #经典案例导入
        suoyin = {'滑翔机':1,'太空船':2,'高斯帕滑翔机枪':3,'蜂王梭':4,'慨影':5}
        suo = suoyin [self.combobox.get()]
        m,n = self.height//2,self.width//2
        
        self.clear()
        if suo ==1:
            for (p,q) in {(0,1),(0,-1),(-1,-1),(1,-1),(1,0)}:
                self.board[m+p][n-q] = ALIVE
                self.running = False
        elif suo ==2:
            for (p,q) in {(-2,0),(-2,1),(-1,0),(-1,1),(-1,2),(0,-1),(0,1),(0,2),(1,-1),(1,0),(1,1),(2,0)}:
                self.board[m+p][n-q] = ALIVE
                self.running = False
        elif suo ==3:
            for (p,q) in {(1,5),(1,6),(2,5),(2,6),(11,5),(11,6),(11,7),
            (12,4),(12,8),(13,3),(13,9),(14,3),(14,9),(15,6),(16,4),(16,8),
            (17,5),(17,6),(17,7),(18,6),(21,3),(21,4),(21,5),(22,3),(22,4),
            (22,5),(23,2),(23,6),(25,1),(25,2),(25,6),(25,7),(35,3),(35,4),
            (36,3),(36,4)}:
                self.board[p][q] = ALIVE
                self.running = False
        elif suo ==4:
            for (p,q) in {(-9,0),(-9,-1),(-8,0),(-8,-1),(-4,0),(-3,-1),(-3,1),
            (-2,-2),(-2,2),(-1,-1),(-1,0),(-1,1),(0,-2),(0,-3),(0,2),(0,3),
            (11,-1),(11,0),(12,-1),(12,0)}:
                self.board[m+p][n-q] = ALIVE
                self.running = False
        elif suo ==5:
            for (p,q) in {(-1,2),(-1,3),(-1,6),(-1,-3),(-1,-4),(-1,-7),(0,1),
            (0,2),(0,3),(0,7),(0,-2),(0,-3),(0,-4),(0,-8),
            (1,2),(1,3),(1,6),(1,-3),(1,-4),(1,-7)}:
                self.board[m+p][n-q] = ALIVE
                self.running = False
        self.draw_board()

####游戏逻辑相关#############################################################################################################################################################################################################################################

    def handle_click(self, event):
        # 处理鼠标点击事件,设置细胞状态
        x, y = event.x // self.cell_size, event.y // self.cell_size
        if self.board[x][y] == DEAD:
            self.board[x][y] = ALIVE
        else:
            self.board[x][y] = DEAD
        self.draw_board()

    def draw_board(self):
        # 绘制游戏界面
        self.canvas.delete("all")
        for x in range(self.width):
            for y in range(self.height):
                color = COLORS[self.board[x][y]]
                self.canvas.create_rectangle(x*self.cell_size, y*self.cell_size,
                                             (x+1)*self.cell_size, (y+1)*self.cell_size,
                                             fill=color, outline="gray")

    def start(self):
        # 开始演化
        self.running = True
        self.evolve()

    def pause(self):
        # 暂停演化
        self.running = False

    def clear(self):
        # 清空细胞
        self.board = [[DEAD for _ in range(self.height)] for _ in range(self.width)]
        self.draw_board()

    def random(self):
        # 随机生成初始细胞
        global pran
        for i in range(self.height):
            for j in range(self.width):
                rnd = random.random()
                if rnd > 1-pran:
                    self.board[i][j]=ALIVE
        self.draw_board()

    def count_neighbors(self, x, y):
        # 计算某个细胞周围的活细胞数量
        count = 0
        for i in range(-1, 2):
            for j in range(-1, 2):
                if i == 0 and j == 0:  #除去自身
                    continue
                if x+i < 0 or x+i >= self.width or y+j < 0 or y+j >= self.height: #边界处理
                    continue
                if self.board[x+i][y+j] == ALIVE:
                    count += 1
        return count

    def evolve(self):
        # 演化一次
        if not self.running:
            return
        new_board = [[DEAD for _ in range(self.height)] for _ in range(self.width)]
        for x in range(self.width):
            for y in range(self.height):
                count = self.count_neighbors(x, y)
                if self.board[x][y] == ALIVE:
                    if count < 2 or count > 3:
                        new_board[x][y] = DEAD
                    else:
                        new_board[x][y] = ALIVE
                else:
                    if count == 3:
                        new_board[x][y] = ALIVE
        self.board = new_board
        self.draw_board()
        if not self.onestepp:
            global speed
            self.root.after(1000//speed, self.evolve)#100ms演化一次
        else:
            self.onestepp = False

    def run(self):
        # 运行游戏
        self.draw_board()
        self.root.mainloop()

############################################################################################################################################################################################################################################################

game = GameOfLife(60,60,15)
game.run()

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值