100行代碼實現生命遊戲

生命遊戲是啥呢?名字看起來似乎有點不明覺厲,讓人首先聯想到這貨,


100行代碼實現生命遊戲


其實並不是,這裡說的生命遊戲是指:Conway’s Game of Life。這是什麼東西呢?就是根據生物的特性,用一種簡單的規則模擬生物種群的狀態。我們中學都學過生物課,生物種群有產生,擴大和消亡幾種狀態。產生和擴大一般是因為合適條件下種群繁殖,消亡是因為種群密度過大或者競爭導致的資源不足以維持種群。我們用空格子表示種群生活的空間(消亡),用有顏色的格子表示活著的種群(存活),這裡將種群擴張和消亡的規則簡單化抽象為下面幾條:

如果一個格子是存活的:

如果周圍的格子只有1個以及0個存活,則這個格子狀態變成消亡

如果周圍的格子有4個以及4個以上存活,則這個格子狀態變成消亡

如果周圍的格子有2個或者3個存活,則這個格子狀態繼續保持存活

如果一個格子是消亡的:

如果周圍的格子有3個存活,則這個格子狀態變為存活

可以看出,這是模擬的生物種群,如果數量太少,無法有效繁衍下去,如果數量太多,資源不夠消亡,只有數量剛剛好能夠繼續存活。

生命遊戲規則非常簡單,但是在簡單的規則下,卻能衍生出各種複雜和神奇的圖形。比如常見的固定圖形:

100行代碼實現生命遊戲

複雜點的能移動的“滑翔機”:


100行代碼實現生命遊戲

更復雜的可以“繁殖”的“滑翔者槍”:


100行代碼實現生命遊戲

這樣能衍生出更加複雜的圖形,根據這些規律很多人構造出了令人難以置信的複雜圖形。

有人可能會問,難道這只是無聊人士的消磨腦力之作嗎?並不是,這雖然早期只是一種有趣的研究。但因為這種簡單規則下產生複雜結果的過程,除了本身的魅力,更是能模擬很多自然的現象。所以基於此遊戲,誕生了一個專門的學科分類:元胞自動機(cellular automata,CA)。本來二維的生命遊戲,被擴展到一維和三維上,也出現了更多的規則,可以模擬更復雜的自然現象。比如,用一維元胞自動機模擬道路交通,二維三維元胞自動機模擬混沌系統的演化,等等。

說了這麼多,大家肯定想試試看自己能不能生成什麼更有趣的圖形。我這裡提供一個最簡單的python版生命遊戲,圖形使用自帶的tkinter,因為本身圖形性能問題,格子多了就會比較慢,所以我初始化用了10%的存活格子。下面就是截圖:

100行代碼實現生命遊戲

這裡是代碼,100x100的大小,10%的初始隨機存活格子。如果想生成更大的圖,初始化更多的存活格子,可以使用sdl,pygame,opengl等更高效的繪圖庫,來替換內置的tkinter的canvas。代碼整個72行,可以說非常簡潔了。

<code>import tkinter
import time
import threading
import random


class Game:
def __init__(self):
self.width=100
self.height=100
self.pool = [0]*self.width*self.height
self.pool_bak = [0]*self.width*self.height
self.block_size = 5
self.pixels = []
self.win = tkinter.Tk()
self.win.title('Conway’s Game of Life')
self.canvas = tkinter.Canvas(self.win, width=self.width*self.block_size,height=self.height*self.block_size,bg="white")
self.canvas.pack()
self.random(10)
self.worker = threading.Thread(target = self.run)
self.worker.start()
self.win.mainloop()
def random(self,rate):
for y in range(0,self.height):
for x in range(0,self.width):
if random.randint(0,100)<=rate:
self.pool[self.width*y+x]=1
def get_nearby(self,x,y):
t = [-1,0,1]
return [(x+m,y+n) for m in t for n in t if x+m < self.width and x-m>=0 and y+n<self.height>=0 and not (m==0 and n==0)]
def get_nearby_count(self,x,y):
t = self.get_nearby(x,y)
count = 0
for i in t:
idx = self.width*i[1]+i[0]
if self.pool[idx] >0:
count+=1
return count
def rule(self,x,y):

c = self.get_nearby_count(x,y)
if c<=1:
return 0
if c>=4:
return 0
if c==3:
return 1
if c==2:
return self.pool[self.width*y+x]
def swap(self):
tmp = self.pool_bak
self.pool_bak=self.pool
self.pool=tmp
def draw(self):
self.canvas.delete(tkinter.ALL)
for y in range(0,self.height):
for x in range(0,self.width):
if self.pool[self.width*y+x]==1:
self.canvas.create_rectangle(x*self.block_size,y*self.block_size,(x+1)*self.block_size,(y+1)*self.block_size,fill= 'blue')
self.canvas.update()
def change(self):
for y in range(0,self.height):
for x in range(0,self.width):
self.pool_bak[self.width*y+x] = self.rule(x,y)
self.swap()
def run(self):
while True:
self.draw()
self.change()
time.sleep(0.02)

if __name__=="__main__":
g=Game()

/<self.height>/<code>


分享到:


相關文章: