zoukankan      html  css  js  c++  java
  • python实现简单动画——生命游戏

    生命游戏

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

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

    其简单动画效果如:

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

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

    控制光标位置是通过ANSI转义符实现的。从这篇文章获得相关知识的:https://www.zhihu.com/question/21100416/answer/208143599

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

    全部代码如下:

      1 import os
      2 import sys
      3 import time
      4 import random
      5 from collections import namedtuple
      6 
      7 
      8 ALIVE = '*'
      9 EMPTY = ' '
     10 
     11 
     12 Query = namedtuple('Query', ('y', 'x'))
     13 
     14 def count_neighbors(y, x):
     15     n_ = yield Query(y + 1, x + 0)  # North
     16     ne = yield Query(y + 1, x + 1)  # Northeast
     17     e_ = yield Query(y + 0, x + 1)  # East
     18     se = yield Query(y - 1, x + 1)  # Southeast
     19     s_ = yield Query(y - 1, x + 0)  # South
     20     sw = yield Query(y - 1, x - 1)  # Southwest
     21     w_ = yield Query(y + 0, x - 1)  # West
     22     nw = yield Query(y + 1, x - 1)  # Northwest
     23     neighbor_states = [n_, ne, e_, se, s_, sw, w_, nw]
     24     count = 0
     25     for state in neighbor_states:
     26         if state == ALIVE:
     27             count += 1
     28     return count
     29 
     30 Transition = namedtuple('Transition', ('y', 'x', 'state'))
     31 
     32 def step_cell(y, x):
     33     state = yield Query(y, x)
     34     neighbors = yield from count_neighbors(y, x)
     35     next_state = game_logic(state, neighbors)
     36     yield Transition(y, x, next_state)
     37 
     38 
     39 def game_logic(state, neighbors):
     40     if state == ALIVE:
     41         if neighbors < 2:
     42             return EMPTY     # Die: Too few
     43         elif neighbors > 3:
     44             return EMPTY     # Die: Too many
     45     else:
     46         if neighbors == 3:
     47             return ALIVE     # Regenerate
     48     return state
     49 
     50 
     51 TICK = object()
     52 
     53 def simulate(height, width):
     54     while True:
     55         for y in range(height):
     56             for x in range(width):
     57                 yield from step_cell(y, x)
     58         yield TICK
     59 
     60 
     61 class Grid(object):
     62     def __init__(self, height, width):
     63         self.height = height
     64         self.width = width
     65         self.rows = []
     66         for _ in range(self.height):
     67             self.rows.append([EMPTY] * self.width)
     68 
     69     def query(self, y, x):
     70         return self.rows[y % self.height][x % self.width]
     71 
     72     def assign(self, y, x, state):
     73         self.rows[y % self.height][x % self.width] = state
     74 
     75     def random_alive(self, live_count):
     76         xy = [(i,j) for i in range(self.width) for j in range(self.height)]
     77         for i,j in random.sample(xy, live_count):
     78             self.assign(i, j, ALIVE)
     79 
     80     def live_a_generation(self,grid, sim):
     81         # self.change_state(EMPTY)
     82         progeny = Grid(grid.height, grid.width)
     83         item = next(sim)
     84         while item is not TICK:
     85             if isinstance(item, Query):
     86                 state = grid.query(item.y, item.x)
     87                 item = sim.send(state)
     88             else:  # Must be a Transition
     89                 progeny.assign(item.y, item.x, item.state)
     90                 item = next(sim)
     91         return progeny
     92 
     93     def __str__(self):
     94         output = ''
     95         for row in self.rows:
     96             for cell in row:
     97                 output += cell
     98             output += '
    '
     99         return output.strip()      
    100 
    101 
    102 def main(x,y,k):
    103     os.system('cls') # linux 为 clear
    104     grid = Grid(x, y)
    105     grid.random_alive(k)
    106     clear = 'x1b[{}Ax1b[{}D'.format(x,y)
    107     print(grid, end='')
    108     sim = simulate(grid.height, grid.width)
    109     while 1:
    110         time.sleep(.1)
    111         grid = grid.live_a_generation(grid, sim)
    112         print(clear)
    113         print(grid, end='')
    114         time.sleep(.1)
    115         print(clear)
    116 
    117 if __name__ == '__main__':
    118     main(30,40,205)
  • 相关阅读:
    Symmetric Order
    Red and Black
    Sticks(递归经典)
    Pascal Library
    cantor的数表
    OJ 调试技巧:VS2010 中 通过设置编译参数定义宏,无需修改源文件重定向标准输入输出
    strcmp
    最短周期串
    字母重排
    codeblocks 单步调试
  • 原文地址:https://www.cnblogs.com/huanping/p/9890668.html
Copyright © 2011-2022 走看看