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 += '\n'
 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[{}A\x1b[{}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)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM