zoukankan      html  css  js  c++  java
  • Python实现生命游戏

    • 1. 生命游戏是什么

    生命游戏是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。它包括一个二维矩形世界,这个世界中的每个方格居住着一个活着的或死了的细胞。一个细胞在下一个时刻生死取决于相邻八个方格中活着的或死了的细胞的数量。如果相邻方格活着的细胞数量过多,这个细胞会因为资源匮乏而在下一个时刻死去;相反,如果周围活细胞过少,这个细胞会因太孤单而死去。

    规则看起来很简单,但却能演绎出无穷无尽的内容。

    滑翔者:每4个回合"它"会向右下角走一格。虽然细胞早就是不同的细胞了,但它能保持原本的形态。

    轻量级飞船:它的周期是4,每2个回合会向右边走一格。

    脉冲星:它的周期为3,看起来像一颗周期爆发的星星。

    更复杂的图案。

    来体会一下这些作品的脑洞以及震撼:

    史诗般的生命游戏http://www.iqiyi.com/w_19rsq435c9.html

    用生命游戏实现生命游戏:http://www.bilibili.com/video/av616329/index.html

    • 2. Console版:一个简单的Python实现

    生命游戏的规则其实很简单。我们可以把计算机中的宇宙想象成是一堆方格子构成的封闭空间,尺寸为N的空间就有N*N个格子。而每一个格子都可以看成是一个生命体,每个生命都有生和死两种状态,如果该格子生就显示蓝色,死则显示白色。每一个格子旁边都有邻居格子存在,如果我们把3*3的9个格子构成的正方形看成一个基本单位的话,那么这个正方形中心的格子的邻居就是它旁边的8个格子。

    每个格子的生死遵循下面的原则:

    1. 如果一个细胞周围有3个细胞为生(一个细胞周围共有8个细胞),则该细胞为生(即该细胞若原先为死,则转为生,若原先为生,则保持不变) 。

    2. 如果一个细胞周围有2个细胞为生,则该细胞的生死状态保持不变;

    3. 在其它情况下,该细胞为死(即该细胞若原先为生,则转为死,若原先为死,则保持不变)

    设定图像中每个像素的初始状态后依据上述的游戏规则演绎生命的变化,由于初始状态和迭代次数不同,将会得到令人叹服的优美图案。

    我们用#代表活的细胞,空格表示死的细胞,那么我们可以用控制台打印字符、清屏来模拟生命游戏。我的代码在github上:

    https://github.com/Pleiades0428/GameOfLife/blob/master/Demo/gameOfLife.py

    游戏世界尺寸为60x20,随机生成初始状态,循环边界,按任意键进入下一帧,q退出。

    单纯的看这段程序,好像并没有什么问题,代码逻辑正确、清晰。

    效果图:

    • 3. Python列表生成式

    我们来尝试一些python的高级特性,比如列表生成式。

    例如,在生成初始值时,我们一般这样写:

     1 screen = []
     2 width = 60
     3 height = 20
     4 def Init():
     5     for i in range(height):
     6         line = []
     7         for j in range(width):
     8             if random.random() > 0.8:
     9                 line.append('#')
    10             else:
    11                 line.append(' ')
    12         screen.append(line)

    如果用列表生成式,我们可以这样写:

    1 def Init():
    2     global screen
    3     screen = [['#' if random.random() > 0.8 else ' ' for i in range(width)] for j in range(height)]

    注意这里必须用global声明,否则screen将默认作为函数内的局部变量。这里用了两层列表生成式来生成一个二维数组。

    列表生成式很好很强大,如果用好能大大提高效率。但会牺牲一定的可读性,如果单个表达式写的过于复杂,那就变成write-only了。尤其是在团队开发情况下,可读性日益重要。

    重写后的代码:

    https://github.com/Pleiades0428/GameOfLife/blob/master/Demo/gameOfLife.1.py

    如果仅仅是作为练习,这样就已经足够好了,简洁易读。

    • 4. 重构:面向对象与重用

    可是我们还不能满足,我们来给生命插上面向对象的翅膀,在模块化的天空中翱翔。对,就是让他跟别的模块搞对象!

    先来定义一个类GameOfLifeWorld,之前那些丑陋的全局变量,让他们统统变成成员变量,再也不能在外兴风作浪。

    class GameOfLifeWorld:
    
        width = 100
        height = 100
        cells = []
    …略

    然后把UI层剥离,只保留游戏的核心逻辑。

    代码:

    https://github.com/Pleiades0428/GameOfLife/blob/master/Demo/gameOfLifeWorld.py

    • 5. GUI:Tkinter的调用

    有了上一步的铺垫,我们终于可以让Tkinter粉墨登场了。Tkinter是著名的UI库,Python自带的Tkinter是一个精简版,不过也够我们用的了。

    我们这里用到的主要是Canvas,Button控件。Canvas画布用来绘制游戏区,Button用来交互。

    代码:

    https://github.com/Pleiades0428/GameOfLife/blob/master/Demo/gameOfLifeWorld.py

    效果:

    以上就是这样,项目我还会继续改进,希望大家喜欢。

  • 相关阅读:
    java客户端集成RocketMq
    java8常见流式操作
    Spring源码架构以及编译
    Rocket消息存储原理
    由二叉树中序和先序遍历求二叉树的结构
    10.14重写ENqUEUE和DEQUEUE,使之能处理队列的下溢和上溢。
    10.12 说明如何用一个数组A[1..n]来实现两个栈,使得两个栈中的元素总数不到n时,两者都不会发生上溢,注意PUSH和POP操作的时间应为O(1)。
    用类模板实现对任何类型的数据进行堆栈进行存取操作。
    java struts2+urlrewrite 配置404错误
    c++ sizeof 及别名定义2种示例
  • 原文地址:https://www.cnblogs.com/pleiades/p/8353713.html
Copyright © 2011-2022 走看看