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>


分享到:


相關文章: