美文网首页
Python简单动画——生命游戏

Python简单动画——生命游戏

作者: 何物昂 | 来源:发表于2019-08-08 23:37 被阅读0次

    生命游戏

    生命游戏的宇宙是一个无限的,其中细胞的二维正交网格,每个细胞处于两种可能的状态之一,即活着或死亡(分别是人口稠密和无人居住)。每个细胞与它的八个邻居相互作用,这八个邻居是水平,垂直或对角相邻的细胞。在每一步中,都会发生以下转换:

    • 任何有两个以上活着的邻居的活细胞都会死亡,好像是在人口下一样。
    • 任何有两三个活着的邻居的活细胞都会生活在下一代。
    • 任何有三个以上活着的邻居的活细胞都会死亡,就好像人口过剩一样。
    • 任何具有三个活的邻居的死细胞都会变成一个活细胞,就像是通过繁殖一样。

    其简单动画效果如:


    life-game.gif

    代码实现

    代码逻辑

    其主要实现逻辑代码出自Effective Python一书中。不过原代码中的生命游戏是静止的,把每一代分别打印出来,没有动画效果,我增加部分代码,实现在终端的动画效果。

    动画效果

    第一代细胞(预设生存环境在 X * Y 的二维平面方格上)随机生成,将其打印在控制台上,然后此时控制台光标会从初始位置(方格左上角(1,1)上)到方格右下角(X,Y)的位置。下一代细胞打印前通过移动控制台的光标到初始位置(1,1)上,此后的打印这代细胞就会覆盖前一代细胞。造成视觉上的动画效果。

    控制光标位置是通过ANSI转义符实现的,参考这个知乎回答

    (n为字符数)
    \x1b[nA]   光标上移
    \x1b[nB]   光标下移
    \x1b[nC]   光标右移
    \x1b[nD]   光标左移
    

    代码

    全部代码(懒得写注释了,游戏逻辑可以参考Effective Python)

    import os
    import sys
    import time
    import random
    from collections import namedtuple
    
    
    ALIVE = '*'
    EMPTY = ' '
    
    
    Query = namedtuple('Query', ('y', 'x'))
    
    def count_neighbors(y, x):
        n_ = yield Query(y + 1, x + 0)  # North
        ne = yield Query(y + 1, x + 1)  # Northeast
        e_ = yield Query(y + 0, x + 1)  # East
        se = yield Query(y - 1, x + 1)  # Southeast
        s_ = yield Query(y - 1, x + 0)  # South
        sw = yield Query(y - 1, x - 1)  # Southwest
        w_ = yield Query(y + 0, x - 1)  # West
        nw = yield Query(y + 1, x - 1)  # Northwest
        neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw]
        count = 0
        for state in neighbor_states:
            if state == ALIVE:
                count += 1
        return count
    
    Transition = namedtuple('Transition', ('y', 'x', 'state'))
    
    def step_cell(y, x):
        state = yield Query(y, x)
        neighbors = yield from count_neighbors(y, x)
        next_state = game_logic(state, neighbors)
        yield Transition(y, x, next_state)
    
    
    def game_logic(state, neighbors):
        if state == ALIVE:
            if neighbors < 2:
                return EMPTY     # Die: Too few
            elif neighbors > 3:
                return EMPTY     # Die: Too many
        else:
            if neighbors == 3:
                return ALIVE     # Regenerate
        return state
    
    
    TICK = object()
    
    def simulate(height, width):
        while True:
            for y in range(height):
                for x in range(width):
                    yield from step_cell(y, x)
            yield TICK
    
    
    class Grid(object):
        def __init__(self, height, width):
            self.height = height
            self.width = width
            self.rows = []
            for _ in range(self.height):
                self.rows.append([EMPTY] * self.width)
    
        def query(self, y, x):
            return self.rows[y % self.height][x % self.width]
    
        def assign(self, y, x, state):
            self.rows[y % self.height][x % self.width] = state
    
        def random_alive(self, live_count):
            xy = [(i,j) for i in range(self.width) for j in range(self.height)]
            for i,j in random.sample(xy, live_count):
                self.assign(i, j, ALIVE)
    
        def live_a_generation(self,grid, sim):
            # self.change_state(EMPTY)
            progeny = Grid(grid.height, grid.width)
            item = next(sim)
            while item is not TICK:
                if isinstance(item, Query):
                    state = grid.query(item.y, item.x)
                    item = sim.send(state)
                else:  # Must be a Transition
                    progeny.assign(item.y, item.x, item.state)
                    item = next(sim)
            return progeny
    
        def __str__(self):
            output = ''
            for row in self.rows:
                for cell in row:
                    output += cell
                output += '\n'
            return output.strip()      
    
    
    def main(x,y,k):
        os.system('cls') # linux 为 clear
        grid = Grid(x, y)
        grid.random_alive(k)
        clear = '\x1b[{}A\x1b[{}D'.format(x,y)
        print(grid, end='')
        sim = simulate(grid.height, grid.width)
        while 1:
            time.sleep(.1)
            grid = grid.live_a_generation(grid, sim)
            print(clear)
            print(grid, end='')
            time.sleep(.1)
            print(clear)
    
    if __name__ == '__main__':
        main(30,40,205)
    

    相关文章

      网友评论

          本文标题:Python简单动画——生命游戏

          本文链接:https://www.haomeiwen.com/subject/ojjqjctx.html