康威生命游戏的学习

于实验楼上发现了康威生命游戏。转载生命游戏的实验内容。

康威生命游戏,又称康威生命棋,是英国数学家约翰·何顿·康威在1970年发明的细胞自动机。 它最初于1970年10月在《科学美国人》杂志上马丁·葛登能的“数学游戏”专栏出现。

这里写图片描述

规则

每个细胞有两种状态-存活或死亡,每个细胞与以自身为中心的周围八格细胞产生互动。

人口过少:当周围低于2个(不包含2个)存活细胞时, 本单元活细胞死亡。
稳定:当周围有2个或3个存活细胞时, 本单元细胞保持原样。
人口过剩:当周围有3个以上的存活细胞时,本单元活细胞死亡。
繁殖:当周围有3个存活细胞时,本单元细胞存活/活化。

可能初看觉得这就是个模拟细胞繁衍的东西,规则也很简单,这能有什么意思。

这很有意思。

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

生物图鉴

静物族:面包
这里写图片描述

震荡族:信号灯
这里写图片描述
震荡族:脉冲星
这里写图片描述
宇宙飞船族:LWSS
这里写图片描述
XXX族:质数计算器

(本图出自 golly 的主页:http://golly.sourceforge.net/ golly 是一个跨平台专门用于探索生命游戏与元胞自动机的开源软件)

趣闻:康威曾经相信没有 pattern 能够生成无限多的活细胞,并悬赏 50刀 奖赏能在 1970 年前找到反例的人。结果就是在生命游戏的世界中出现了很多枪支弹药啦。

代码实现

我们会用 pygame 来实现一个生命游戏程序

安装 pygame

$ sudo apt-get update
$ sudo apt-get install python-pygame

我们使用矩阵来记录我们的游戏世界,其中单位值为 0 代表细胞死亡。单位值为 1 代表细胞存活。

感谢 The Game of Life in Python 这篇博文,一行代码解决了计算细胞周围活细胞数量的问题。

nbrs_count = sum(np.roll(np.roll(X, i, 0), j, 1)
                 for i in (-1, 0, 1) for j in (-1, 0, 1)
                 if (i != 0 or j != 0))

由于我们的游戏世界是上下左右循环的,所以将矩阵往8个方向进行循环移位得到8个新矩阵,将8个新矩阵相加就能够得到每个细胞周围的活细胞数量的矩阵了。

np.roll 操作就是循环移位操作。np.roll(X, i, 0) 中的 X 代表输入矩阵,i 代表移位的大小,0 代表移位的维度,np.roll(X, 1, 0) 代表矩阵下移一格,np.roll(X, 1, 1) 代表右移一格,if (i != 0 or j != 0)) 是为了将原矩阵从计算中去除。

通过活细胞数量矩阵根据更新规则更新我们的世界。因为矩阵单位只有两种状态,这里我们只考虑存活态就可以了。注意到存活的条件:

稳定:当周围有2个或3个存活细胞时, 本单元细胞保持原样。
繁殖:当周围有3个存活细胞时,本单元细胞存活/活化。

即细胞周围数量等于3 或者 本单元细胞存活的同时周围有2个存活细胞的时候。本单元细胞将在下一代存活(也可看作繁衍)

翻译过来就是:

(nbrs_count == 3) | (X & (nbrs_count == 2))

注意到这种方法虽然便捷,但显然效率不怎么样。因为这种做法更新了矩阵的每一个单元,这完全没有必要,大部分情况下矩阵都是稀疏的。但如何改进就不在本文的讨论范畴内了。欢迎读者自己尝试提升效率。

我们实现的生命游戏操作如下:

R键 :重置世界
回车键 :进行演化
空格键 :暂停演化
鼠标左键 :增添一个细胞
鼠标右键 :销毁一个细胞

下面是用 pygame 实现的全部代码。

#-*- coding:utf8 -*-
import pygame, sys, time
import numpy as np
from pygame.locals import *

#矩阵宽与矩阵高
WIDTH = 80
HEIGHT = 40

#记录鼠标按键情况的全局变量
pygame.button_down = False

#记录游戏世界的矩阵
pygame.world=np.zeros((HEIGHT,WIDTH))

#创建 Cell 类方便细胞绘制
class Cell(pygame.sprite.Sprite):

    size = 10

    def __init__(self, position):

        pygame.sprite.Sprite.__init__(self)

        self.image = pygame.Surface([self.size, self.size])

        #填上白色
        self.image.fill((255,255,255))

        # 创建一个以左上角为锚点的矩形
        self.rect = self.image.get_rect()
        self.rect.topleft = position

#绘图函数,注意到我们是把画布重置了再遍历整个世界地图,因此有很大的性能提升空间
def draw():
    screen.fill((0,0,0))
    for sp_col in range(pygame.world.shape[1]):
        for sp_row in range(pygame.world.shape[0]):
            if pygame.world[sp_row][sp_col]:
                new_cell = Cell((sp_col * Cell.size,sp_row * Cell.size))
                screen.blit(new_cell.image,new_cell.rect)

#根据细胞更新规则更新地图
def next_generation():
    nbrs_count = sum(np.roll(np.roll(pygame.world, i, 0), j, 1)
                 for i in (-1, 0, 1) for j in (-1, 0, 1)
                 if (i != 0 or j != 0))

    pygame.world = (nbrs_count == 3) | ((pygame.world == 1) & (nbrs_count == 2)).astype('int')

#地图初始化
def init():
    pygame.world.fill(0)
    draw()
    return 'Stop'

#停止时的操作
def stop():
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()

        if event.type == KEYDOWN and event.key == K_RETURN:
            return 'Move'

        if event.type == KEYDOWN and event.key == K_r:
            return 'Reset'

        if event.type == MOUSEBUTTONDOWN:
            pygame.button_down = True
            pygame.button_type = event.button

        if event.type == MOUSEBUTTONUP:
            pygame.button_down = False

        if pygame.button_down:
            mouse_x, mouse_y = pygame.mouse.get_pos()

            sp_col = mouse_x / Cell.size;
            sp_row = mouse_y / Cell.size;

            if pygame.button_type == 1: #鼠标左键
                pygame.world[sp_row][sp_col] = 1
            elif pygame.button_type == 3: #鼠标右键
                pygame.world[sp_row][sp_col] = 0
            draw()

    return 'Stop'

#计时器,控制帧率
pygame.clock_start = 0


#进行演化时的操作
def move():
    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()
            sys.exit()
        if event.type == KEYDOWN and event.key == K_SPACE:
            return 'Stop'
        if event.type == KEYDOWN and event.key == K_r:
            return 'Reset'
                if event.type == MOUSEBUTTONDOWN:
            pygame.button_down = True
            pygame.button_type = event.button

        if event.type == MOUSEBUTTONUP:
            pygame.button_down = False

        if pygame.button_down:
            mouse_x, mouse_y = pygame.mouse.get_pos()

            sp_col = mouse_x / Cell.size;
            sp_row = mouse_y / Cell.size;

            if pygame.button_type == 1:
                pygame.world[sp_row][sp_col] = 1
            elif pygame.button_type == 3:
                pygame.world[sp_row][sp_col] = 0
            draw()


    if time.clock() - pygame.clock_start > 0.02:
        next_generation()
        draw()
        pygame.clock_start = time.clock()

    return 'Move'



if __name__ == '__main__':

    #状态机对应三种状态,初始化,停止,进行
    state_actions = {
            'Reset': init,
            'Stop': stop,
            'Move': move
        }
    state = 'Reset'

    pygame.init()
    pygame.display.set_caption('Conway\'s Game of Life')

    screen = pygame.display.set_mode((WIDTH * Cell.size, HEIGHT * Cell.size))

    while True: # 游戏主循环

        state = state_actions[state]()
        pygame.display.update()

这里写图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值