Python 实现生命游戏
這次我們使用 Python 來(lái)實(shí)現(xiàn)生命游戲,這是一種簡(jiǎn)單的元胞自動(dòng)機(jī)。基于一定規(guī)則,程序可以自動(dòng)從當(dāng)前狀態(tài)推演到下一狀態(tài)。制作的成品如下:
先來(lái)說(shuō)說(shuō)生命游戲的規(guī)則:
在生命游戲中,每個(gè)單元格有兩種狀態(tài),生與死。在我們的實(shí)現(xiàn)中,黃色的單元格代表活著的細(xì)胞,紅色單元格表示死亡的細(xì)胞。而每一個(gè)細(xì)胞的下一狀態(tài),是由該細(xì)胞及周圍的八個(gè)細(xì)胞的當(dāng)前狀態(tài)決定的。
具體而言:
當(dāng)前細(xì)胞為活細(xì)胞
當(dāng)前細(xì)胞為死細(xì)胞
- 周圍恰好三個(gè)活細(xì)胞,下一世代,活細(xì)胞將繁殖到該單元格。
所需模塊
無(wú)需安裝的標(biāo)準(zhǔn)庫(kù):
- argparse(命令行參數(shù))
- enum(枚舉)
第三方庫(kù):
- numpy
- matplotlib
導(dǎo)入模塊:
# 關(guān)注微信公眾號(hào):Python高效編程,后臺(tái)回復(fù)`2019527`,獲取源代碼。 import argparse from enum import IntEnum import matplotlib.pyplot as plt import matplotlib.animation as animation # 制作動(dòng)圖 import numpy as np編程要點(diǎn)
首先,我們要知道細(xì)胞的生存空間是 N * N 的方陣,每個(gè)細(xì)胞都有兩種狀態(tài):on, off。on 為 255,off 為 0。我們使用 numpy 產(chǎn)生 N * N 的方陣。np.random.choice 是在 State.on 和 State.off ,等概率隨機(jī)抽取一個(gè)元素構(gòu)造 N * N 的方陣。
class State(IntEnum):on = 255off = 0def random_data(length = 4, seed = 420) -> np.array:np.random.seed(seed)return np.random.choice([State.off, State.on], size=(length, length), p=[0.5, 0.5])其次我們要明白如何計(jì)算細(xì)胞周圍活細(xì)胞的個(gè)數(shù),尤其是邊界一圈的細(xì)胞。我們可以采用余數(shù)的方式,假設(shè)棋盤(pán)大小為 9 * 9,那么對(duì)于左右邊界而言,左邊界的左邊一個(gè)元素的計(jì)算方式: - 1 % 9 = 8,自動(dòng)折到右邊界上。將細(xì)胞周圍八個(gè)單元格的數(shù)值加起來(lái),除以 255,就可以得到細(xì)胞周圍活細(xì)胞的個(gè)數(shù)。
def _count(data, row, col):shape = data.shape[0]up = (row - 1) % shapedown = (row + 1) % shaperight = (col + 1) % shapeleft = (col - 1) % shapereturn (data[up, right] + data[up, left] +data[down, right] + data[down, left] +data[row, right] + data[row, left] +data[up, col] + data[down, col]) // 255接下來(lái)是對(duì)規(guī)則的翻譯,即根據(jù)當(dāng)前世代的狀態(tài),推演出下一世代,細(xì)胞的狀態(tài)。initial 為當(dāng)前世代的矩陣,data為下一世代的矩陣,我們根據(jù) initial 的數(shù)值,計(jì)算出 data 的數(shù)值。total 為周圍活細(xì)胞的個(gè)數(shù),如果當(dāng)前為活細(xì)胞,total 大于三或者小于二,下一世代就會(huì)死去。如果當(dāng)前為死細(xì)胞,total 等于三,下一世代活細(xì)胞就會(huì)繁殖到該單元格上。
def count(initial, data, row, col):total = _count(initial, row, col)if initial[row, col]:if (total < 2) or (total > 3):data[row, col] = State.offelse:if total == 3:data[row, col] = State.on接下來(lái)是制作動(dòng)圖的過(guò)程,前面幾行是繪圖的基本操作。之后,我們使用到了 matplotlib.animation 的方法。其中,FuncAnimation 接受的參數(shù)含義:fig 為圖像句柄,generate 函數(shù)是我們更新每一幀圖像所需數(shù)據(jù)的函數(shù),下面會(huì)有介紹,fargs 為 genrate 函數(shù)的除去第一個(gè)參數(shù)的其他參數(shù),第一個(gè)參數(shù)由 FuncAnimation 指定 framenum(幀數(shù)) 傳給 generate 函數(shù)。frames 是幀數(shù),interval 是更新圖像間隔,save_count 為從幀到緩存的值的數(shù)量。
如果指定保存路徑(html),則保存為 html 動(dòng)畫(huà)。
# 關(guān)注微信公眾號(hào):Python高效編程,后臺(tái)回復(fù)`2019527`,獲取源代碼。 def update(data, save_name):update_interval = 50fig, ax = plt.subplots()ax.set_xticks([])ax.set_yticks([])img = ax.imshow(data, cmap='autumn', interpolation='nearest')ani = animation.FuncAnimation(fig, generate, fargs=(img, plt, data),frames=20,interval=update_interval,save_count=50)if save_name:ani.save(save_name, fps=30, extra_args=['-vcodec', 'libx264'])plt.show()下面我們來(lái)看 generate 函數(shù),NUM 為當(dāng)?shù)螖?shù),frame_num 接收來(lái)自 FuncAnimation 的幀數(shù)。通過(guò)嵌套的 for 循環(huán),我們逐個(gè)地更新方陣中各元素的狀態(tài)。
NUM = 0 # 關(guān)注微信公眾號(hào):Python高效編程,后臺(tái)回復(fù)`2019527`,獲取源代碼。 def generate(frame_num, img, plt, initial):global NUMNUM += 1plt.title(f'{NUM} generation')data = initial.copy()rows, cols = data.shapefor row in range(rows):for col in range(cols):count(initial, data, row, col)img.set_data(data)initial[:] = data[:]return img最后,我們可以通過(guò)命令行參數(shù),運(yùn)行我們的程序:
– size 參數(shù)為棋盤(pán)大小,–seed 為隨機(jī)種子,用于產(chǎn)生不同的隨機(jī)方陣。
python conway.py --size 50 --seed 18高斯帕滑翔機(jī)槍(Gosper Glider Gun)
可將 --gosper 更改為 --glider 滑翔機(jī)。–save 為動(dòng)圖保存的地址。
python conway.py --size 80 --gosper --save gosper.html
關(guān)注微信公眾號(hào):Python高效編程,后臺(tái)回復(fù)2019527,獲取源代碼。
總結(jié)
以上是生活随笔為你收集整理的Python 实现生命游戏的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Nvidia Jetson AGX Or
- 下一篇: 执行python generate_tf