10.27 用Python模擬生物繁衍進化(康威的生命遊戲)

康威的生命遊戲(Python實現)

Conways的生命遊戲是由John Conway創建的蜂窩自動化方法。這個遊戲是以生物學為基礎創建的,但已應用於各種領域,如圖形,地形生成等。

用Python模擬生物繁衍進化(康威的生命遊戲)

生命遊戲(Game of Life)沒有遊戲玩家各方之間的競爭,也談不上輸贏,可以把它歸類為仿真遊戲。事實上,也是因為它模擬和顯示的圖像看起來頗似生命的出生和繁衍過程而得名為“生命遊戲”。在遊戲進行中,雜亂無序的細胞會逐漸演化出各種精緻、有形的結構;這些結構往往有很好的對稱性,而且每一代都在變化形狀。一些形狀一經鎖定就不會逐代變化。有時,一些已經成形的結構會因為一些無序細胞的“入侵”而被破壞。但是形狀和秩序經常能從雜亂中產生出來。

生命遊戲是一個二維網格遊戲,這個網格中每個方格居住著一個活著或死了的細胞。一個細胞在下一個時刻的生死取決於相鄰8個方格中活著或死了的細胞的數量。如果相鄰方格活著的細胞數量過多,這個細胞會因為資源匱乏而在下一個時刻死去;相反,如果周圍活細胞過少,這個細胞會因為孤單而死去。在遊戲初始階段,玩家可以設定周圍活細胞(鄰居)的數目和位置。如果鄰居細胞數目設定過高,網格中大部分細胞會因為找不到資源而死去,直到整個網格都沒有生命;如果鄰居細胞數目設定過低,世界中又會因為生命稀少而得不到繁衍。實際中,鄰居細胞數目一般選取2或者3;這樣整個生命世界才不至於太過荒涼或擁擠,而是一種動態平衡。遊戲規則是:當一個方格周圍有兩個或3個活細胞時,方格中的活細胞在下一個時刻繼續存活;即使這個時刻方格中沒有活細胞,在下一個時刻也會“誕生”活細胞。在這個遊戲中,還可以設定一些更加複雜的規則,例如當前方格的狀態不僅由父一代決定,而且還考慮到祖父一代的情況。

每個方格中都可放置一個生命細胞,每個生命細胞只有兩種狀態:

“生”或“死”。用黑色方格表示該細胞為“生”,空格(白色)表示該細胞為“死”。或者說方格網中黑色部分表示某個時候某種“生命”的分佈圖。生命遊戲想要模擬的是:隨著時間的流逝,這個分佈圖將如何一代一代地變化

“遊戲”是一個零玩家遊戲,意味著它的進化由其初始狀態決定,不需要進一步輸入。通過創建初始配置並觀察其演變方式,或者對於高級“玩家”,通過創建具有特定屬性的模式,可以與生命遊戲進行交互。

遊戲如何運作

因為生命遊戲是建立在九個方格的網格上,所以每個單元格都有八個相鄰的單元格,如給定的圖所示。在網格[i] [j]上訪問模擬中的給定單元(i,j),其中i和j分別是行和列索引。給定時刻的給定小區的值取決於前一時間步的鄰居狀態。康威的生命遊戲有四條規則。

  1. 如果單元格為ON且少於兩個ON的鄰居,則它將關閉
  2. 如果一個單元是ON並且有兩個或三個ON的鄰居,它將保持為ON。
  3. 如果一個單元格為ON並且有三個以上的鄰居處於ON狀態,則它將關閉。
  4. 如果一個單元關閉並且正好有三個接通的鄰居,它將打開。


用Python模擬生物繁衍進化(康威的生命遊戲)


因為我們知道它是如何工作的,接下來我們需要弄清楚如何使其工作。

途徑

1.初始化網格中的單元格。
2.在模擬的每個時間步,在網格中的單元格(i,j),執行以下操作:基於更新單元格(i,j)的值和它的鄰居,考慮到了邊界條件。更新網格值的顯示。

完成這裡之後,讓我們親自動手編寫代碼。

依賴模塊

  1. numpy:用於2D陣列(矩陣)操作。
  2. matplotlib:用於更新模擬或簡單的單詞以使東西移動。
  3. argparse:在代碼中傳遞命令行參數。



現在讓我們開始編寫代碼

# Python code to implement Conway's Game Of Life 
import argparse
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

# setting up the values for the grid
ON = 255
OFF = 0
vals = [ON, OFF]

def randomGrid(N):

"""returns a grid of NxN random values"""
return np.random.choice(vals, N*N, p=[0.2, 0.8]).reshape(N, N)

def addGlider(i, j, grid):

"""adds a glider with top left cell at (i, j)"""
glider = np.array([[0, 0, 255],
[255, 0, 255],
[0, 255, 255]])
grid[i:i+3, j:j+3] = glider

def addGosperGliderGun(i, j, grid):

"""adds a Gosper Glider Gun with top left
cell at (i, j)"""
gun = np.zeros(11*38).reshape(11, 38)

gun[5][1] = gun[5][2] = 255
gun[6][1] = gun[6][2] = 255

gun[3][13] = gun[3][14] = 255
gun[4][12] = gun[4][16] = 255
gun[5][11] = gun[5][17] = 255
gun[6][11] = gun[6][15] = gun[6][17] = gun[6][18] = 255
gun[7][11] = gun[7][17] = 255
gun[8][12] = gun[8][16] = 255

gun[9][13] = gun[9][14] = 255

gun[1][25] = 255
gun[2][23] = gun[2][25] = 255
gun[3][21] = gun[3][22] = 255
gun[4][21] = gun[4][22] = 255
gun[5][21] = gun[5][22] = 255
gun[6][23] = gun[6][25] = 255
gun[7][25] = 255

gun[3][35] = gun[3][36] = 255
gun[4][35] = gun[4][36] = 255

grid[i:i+11, j:j+38] = gun

def update(frameNum, img, grid, N):

# copy grid since we require 8 neighbors
# for calculation and we go line by line
newGrid = grid.copy()
for i in range(N):
for j in range(N):

# compute 8-neghbor sum
# using toroidal boundary conditions - x and y wrap around
# so that the simulaton takes place on a toroidal surface.
total = int((grid[i, (j-1)%N] + grid[i, (j+1)%N] +
grid[(i-1)%N, j] + grid[(i+1)%N, j] +
grid[(i-1)%N, (j-1)%N] + grid[(i-1)%N, (j+1)%N] +
grid[(i+1)%N, (j-1)%N] + grid[(i+1)%N, (j+1)%N])/255)

# apply Conway's rules
if grid[i, j] == ON:
if (total < 2) or (total > 3):
newGrid[i, j] = OFF
else:
if total == 3:
newGrid[i, j] = ON

# update data
img.set_data(newGrid)
grid[:] = newGrid[:]
return img,

# main() function
def main():

# Command line args are in sys.argv[1], sys.argv[2] ..
# sys.argv[0] is the/> # parse arguments
parser = argparse.ArgumentParser(description="Runs Conway's Game of Life simulation.")


# add arguments
parser.add_argument('--grid-size', dest='N', required=False)
parser.add_argument('--mov-file', dest='movfile', required=False)
parser.add_argument('--interval', dest='interval', required=False)
parser.add_argument('--glider', action='store_true', required=False)
parser.add_argument('--gosper', action='store_true', required=False)
args = parser.parse_args()

# set grid size
N = 100
if args.N and int(args.N) > 8:
N = int(args.N)

# set animation update interval
updateInterval = 50
if args.interval:
updateInterval = int(args.interval)

# declare grid
grid = np.array([])

# check if "glider" demo flag is specified
if args.glider:
grid = np.zeros(N*N).reshape(N, N)
addGlider(1, 1, grid)
elif args.gosper:
grid = np.zeros(N*N).reshape(N, N)
addGosperGliderGun(10, 10, grid)

else: # populate grid with random on/off -
# more off than on
grid = randomGrid(N)

# set up animation
fig, ax = plt.subplots()
img = ax.imshow(grid, interpolation='nearest')
ani = animation.FuncAnimation(fig, update, fargs=(img, grid, N, ),
frames = 10,
interval=updateInterval,
save_count=50)

# # of frames?
# set output file
if args.movfile:
ani.save(args.movfile, fps=30, extra_args=['-vcodec', 'libx264'])

plt.show()

# call main

if __name__ == '__main__':
main()

在IDE上運行

不傳遞任何命令行參數,看看效果。

現在讓我們稍微調試一下,讓我們看看如果每500毫秒添加更新動畫並設置尺寸32X32並使用初始滑翔模式會發生什麼。

python'filename.py'--grid-size 32 --interval 500 --glider

您還可以嘗試使用此代碼來操作此代碼以創建不同的模擬。

小夥伴請留步

o(∩_∩)o 喜歡這篇文章或覺得這篇文章有益的小夥伴可以來一波關注和轉發,私信老陳,回覆:001,即可獲得老陳自己錄製或者整理的2018最新的Python人工智能的資料和基礎入門教程。


分享到:


相關文章: