(Python) Conway's Game of Life 威廉生命遊戲
已更新:2021年10月19日
威廉生命遊戲(Conway's Game of Life) 就是細胞自動機的一個例子。有一組規則控制著由離散細胞組成的行為。實際上,會建立一個漂亮的動畫以供查看。 此遊戲的規則如下:
每個細胞有兩種狀態 - 存活或死亡,每個細胞與以自身為中心的周圍8格細胞產生互動
黑色為存活,白色為死亡
當前細胞為存活狀態時,當周圍的存活細胞低於2個時(不包含2個),該細胞變成死亡狀態。
當前細胞為存活狀態時,當周圍有2個或3個存活細胞時,該細胞保持原樣。
當前細胞為存活狀態時,當周圍有超過3個存活細胞時,該細胞變成死亡狀態。
當前細胞為死亡狀態時,當周圍有3個存活細胞時,該細胞變成存活狀態。
威廉生命遊戲中的模式可以移動、自我複製,甚至模仿CPU。在這些複雜的基礎上探究時,會發現其實是由簡單的程式所完成的。我們可以使用串列中再存放串列來代表二維欄位。內部串列代表方格的一欄,方格位置存放#字串代表存活,而放入' ' 空格字串代表死亡。
首先匯入相關模組,讓程式可以使用random.randint()、time.sleep()和copy.deepcopy()等函式
# Conway's game of life
import random
import time
import copy
WIDTH = 60
HEIGHT = 20細胞自動機的第一步是完全隨機的,我們需要建立一個串列的串列資料結構來儲存代表存活細胞或死亡細胞的'#'和' ' 字串。他們在二維串列中的位置就等於在畫面方格上的座標位置。內層的串列代表一欄細胞。起始細胞是用random.randint(0,1)的呼叫,隨機挑選死亡以及存活,機率為各半。
我們把二維串列放在一個名為nextCells的變數中,因為在主程式迴圈的第一步是把nextCells複製到currentCells中。對於這個二維串列資料結構, X座標從左側的0開始,向右遞增,而y座標從上方的0開始,向下遞增。因此,nextCells[0][0]所代表的是畫面左上角的那一格細胞,而nextCells[1][0]則代表其右側的細胞,nextCells[0][1]代表其下方的細胞。
# Create a list of list for the cells
nextCell = []
for x in range(WIDTH):
column = [] # Create a new column
for y in range(HEIGHT):
if random.randint(0, 1) == 0:
column.append('#') # Add a living cell
else:
column.append('') # Add a dead cell
nextCell.append(column) # nextCells is a list of column lists主程式迴圈的每次迭代是細胞自動機的一個步驟。在每個步驟中,我們都會把nextCells複製到currentsCells,在畫面上印出currentCells,然後使用currentCells中的細胞來運算nextCells中細胞的狀態。
while True: # Main program loop
print("\n\n\n\n\n") # Seperate each step with newlines
currentCells = copy.deepcopy(nextCell)兩層的for迴圈可確保在畫面上印出整列細胞的狀態,一列細胞印出之後以print()印出換行。我們對nextCells中的每一列都重複做這項操作。
#Print currentCell on the screen:
for y in range(HEIGHT):
for x in range(WIDTH):
print(currentCells[x][y], end=" ") # print the # or space
print() # Print a newline at the end of the row.接下來,需要使用兩層的for迴圈來運算出下一步每個細胞的狀態。細胞的生死狀態取決於與它相鄰的細胞。因此先計算出目前x和y座標在左、右、上方和下方的細胞方格的索引足標值。
%運算子在這裡能處理起出邊界要「環繞」到另一側的狀況。
最左欄0這個細胞的左側鄰居會是0-1 或 -1,要讓它環繞到最右側一欄的索引足標值59上,我們使用表示式(0-1) %WIDTH來計算,由於WIDTH為60,因此這個表示式的計算結果為59。這種處理技術也適用於右側、上方或下方的相鄰方格座標值。
for x in range (WIDTH):
for y in range(HEIGHT):
# Get neighboring coordinates:
# '%WIDTH' ensures leftCoord is always between 0 and WIDTH-1
leftCoord = (x-1) % WIDTH
rightCoord = (x+1) % WIDTH
aboveCoord = (y-1) % HEIGHT
belowCoord = (y+1) % HEIGHT若想要決定nextCells[x][y]上的細胞是存活或死亡的狀態,這裡需要計算目前細胞currentCells[x][y]所擁有的存活鄰居數量。以下一串的if 陳述句會檢查前細胞方格的8個鄰居狀態,只要有存活的鄰居就對numNeighbors加1
#Count number of living neighbors:
numNeighbor = 0
if currentCells[leftCoord][aboveCoord] == "#":
numNeighbor += 1 # Top-Left neighbor is alive.
if currentCells[x][aboveCoord] == "#":
numNeighbor += 1 # Top neighbor is alive.
if currentCells[rightCoord][aboveCoord] == "#":
numNeighbor += 1 # Top-Right neighbor is alive.
if currentCells[leftCoord][y] == "#":
numNeighbor += 1 # Left neighbor is alive.
if currentCells[rightCoord][y] == "#":
numNeighbor += 1 # Right neighbor is alive.
if currentCells[leftCoord][belowCoord] == "#":
numNeighbor += 1 # Bottom-Left neighbor is alive.
if currentCells[x][belowCoord] == "#":
numNeighbor += 1 # Bottom neighbor is alive.
if currentCells[rightCoord][belowCoord] == "#":
numNeighbor += 1 # Bottom-Right neighbor is alive.現在知道了currentCells[x][y]這個細胞所擁有的存活鄰居數量,這樣就能決定要把nextCells[x][y]設為"#" 或 " " 。在我們尋遍所有可能的x和y座標之後,這隻程式會透過呼叫time.sleep(1)來暫停一秒。
# Set cell based on Conway's Game of Life rules:
if currentCells[x][y] == "#" and (numNeighbor == 2 or numNeighbor == 3):
# Living cells with 2 or 3 neighbors stay alive
nextCell[x][y] = "#"
elif currentCells[x][y] == ' ' and numNeighbor == 3:
# Dead cells with 3 neighbors become alive:
nextCell[x][y] = "#"
else:
# Everying else dies or stays dead:
nextCell[x][y] = ' '
time.sleep(1)
留言