2016年11月22日 星期二

Conway's Game of Life in Python(使用Python實現Conway的生命遊戲)

    這篇文章提供一Python範例程式來實現Conway的生命遊戲,並使用Pygame套件實現出動畫效果。

在1970年,英國數學家約翰·康威(John Conway)創造了他的“生命遊戲”。
這是一組規則,在一殖民地裡模仿生物有機體的混亂和生長及死亡。
在“遊戲”裡由“活的”和“死的”細胞單元組成,並將演變呈現在網格上。
從這一代過到下一代的規則如下:
     在網格上以該細胞為中心計算其八個相鄰的細胞,

1.假設人口過剩:如果該"活細胞"被三個以上的活細胞包圍,它就會死亡。
2.停滯:如果活細胞被兩個或三個活細胞包圍,它可以繼續存活。
3.如果人口過少:如果活細胞被少於兩個活細胞包圍,它也會死亡。
4.繁殖:如果死細胞被正好三個細胞包圍,它將變成活細胞。

    在程式裡我們設定0為死細胞,1為活細胞,利用Numpy及Pygame做成演變動畫
透過一直循環執行這些規則,可以出現美麗和意想不到的圖案,
有些已死的區域或是長時間不再變化的區域,經由更長的時間演變
因外來的細胞將再重新活耀起來。這遊戲迷人的地方在於它不完全是
由隨機亂數產生,他有上述的幾個規則存在,但是結果又是那麼不可預期,
好像是真的生命細胞在自己演化一般,然而實質上這只是一段程式代碼。


<圖一>隨機產生的細胞自由演化






<圖二>透過左下方的矩陣值變化可以看出上述的四個規則



<圖三>在程式裡內建幾個知名的樣本圖,下面這是Plusar 圖騰

<圖四>下面這是glider_gun圖騰,產生的小滑翔機,會遊走到別的細胞領域,參與別區細胞
             生與死的演變。




在程式裡預設為使用隨機樣本圖騰,
Randomflag=True
將Randomflag=False
則可以在
pattern=np.array(Pulsar) #change pattern here.
改變其他圖騰樣式。
也可以試著再修改程式碼,試試在領域裡放入多種圖騰看它的變化。
<完整範例程式>

import numpy as np
import pygame,time,sys
from pygame.locals import *

##+++==sample pattern =====
S1 = [[0,0,0,0,0,0],
  [0,0,0,1,0,0],
  [0,1,0,1,0,0],
  [0,0,1,1,0,0],
  [0,0,0,0,0,0],
  [0,0,0,0,0,0]]
  
glider_gun =[[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
 [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1],
 [1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [1,1,0,0,0,0,0,0,0,0,1,0,0,0,1,0,1,1,0,0,0,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
 
diehard = [[0, 0, 0, 0, 0, 0, 1, 0],
           [1, 1, 0, 0, 0, 0, 0, 0],
           [0, 1, 0, 0, 0, 1, 1, 1]]

glider = [[1, 0, 0],
          [0, 1, 1],
          [1, 1, 0]]

r_pentomino = [[0, 1, 1],
               [1, 1, 0],
               [0, 1, 0]]

beacon = [[0, 0, 1, 1],
          [0, 0, 1, 1],
          [1, 1, 0, 0],
          [1, 1, 0, 0]]

acorn = [[0, 1, 0, 0, 0, 0, 0],
         [0, 0, 0, 1, 0, 0, 0],
         [1, 1, 0, 0, 1, 1, 1]]

spaceship = [[0, 0, 1, 1, 0],
             [1, 1, 0, 1, 1],
             [1, 1, 1, 1, 0],
             [0, 1, 1, 0, 0]]
    
    
block_switch_engine = [[0, 0, 0, 0, 0, 0, 1, 0],
                       [0, 0, 0, 0, 1, 0, 1, 1],
                       [0, 0, 0, 0, 1, 0, 1, 0],
                       [0, 0, 0, 0, 1, 0, 0, 0],
                       [0, 0, 1, 0, 0, 0, 0, 0],
                       [1, 0, 1, 0, 0, 0, 0, 0]] 
        
Pulsar= [[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,1,0,0,0,0,1,0,1,0,0,0,0,1,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,1,1,1,0,0,0,1,1,1,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],
 [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]]
##---==sample pattern =====        
def compute_neighbours(Z):
 rows,cols = len(Z), len(Z[0])
 N  = [[0,]*(cols)  for i in range(rows)]
 for x in range(1,cols-1):
  for y in range(1,rows-1):
   N[y][x] = Z[y-1][x-1]+Z[y][x-1]+Z[y+1][x-1] \
     + Z[y-1][x]      +Z[y+1][x] \
     + Z[y-1][x+1]+Z[y][x+1]+Z[y+1][x+1]
 return N

def show(Z):
 for l in Z[1:-1]: 
  print (l[1:-1])


def iterate(Z):
 rows,cols = len(Z), len(Z[0])
 N = compute_neighbours(Z)
 for x in range(1,cols-1):
  for y in range(1,rows-1):
   if Z[y][x] == 1 and (N[y][x] < 2 or N[y][x] > 3):
    Z[y][x] = 0
   elif Z[y][x] == 0 and N[y][x] == 3:
    Z[y][x] = 1
 return Z
 
##test for S1 pattern
for i in range(10):
 print("iterate=",i)
 iterate(S1)
 show(S1) 
 
Randomflag=True
W=100
H=100
##Select pattern sample===========
if Randomflag==True:
 ##==test for Random pattern ==
 Z=np.zeros((W,H))
 S = np.random.randint(0,2,(W,H))
 pattern=np.array(S)    #random pattern sample
 Z[:pattern.shape[0], :pattern.shape[1]] = pattern
 
else:
 ##==test sample pattern =====
 Z=np.zeros((W,H))
 pattern=np.array(Pulsar) #change pattern here.
 Z[30:pattern.shape[0]+30, 30:pattern.shape[1]+30] = pattern

pygame.init()
screen = pygame.display.set_mode((W*6,H*6))
pygame.display.set_caption("Game of Life")
screen.fill((255,255,255))
black = 0,0,0
white=255,255,255
radius = 3
width = 1

while True:
 for event in pygame.event.get():
  if event.type == QUIT:
   sys.exit()
 keys = pygame.key.get_pressed()
 if keys[K_ESCAPE]:
  sys.exit()
 iterate(Z)

 for j in range(W):
  for k in range(H):
   position = j*6,k*6
   if Z[j][k]==1:
    pygame.draw.circle(screen, black, position, radius, width)
   else:
    pygame.draw.circle(screen, white, position, radius, width)
 pygame.display.update()
 time.sleep(0.2)
    
    



歡迎加入FB,AI人工智慧與機器人社團一起討論,
https://www.facebook.com/groups/1852135541678378/2068782386680358/?notif_t=like&notif_id=1477094593187641

加入阿布拉機的3D列印與機器人的FB專頁
https://www.facebook.com/arbu00/


<其他有關A.I文章>
人工神經網路(2)--使用Python實作後向傳遞神經網路演算法(Backprogation artificial neature network)
人工神經網路(1)--使用Python實作perceptron(感知器)
機器學習(1)--使用OPENCV KNN實作手寫辨識

<參考資料:>
https://jakevdp.github.io/blog/2013/08/07/conways-game-of-life/
http://www.conwaylife.com/w/index.php?title=Conway%27s_Game_of_Life