はじめに
セル・オートマトンとして有名なライフゲームが面白そうだったので、Pythonとnumpyでライフゲームのマップ配列を生成するコードを書いてみました。0
(dead)と1
(live)で書かれた初期世代のマップ配列を与えることで、iterativeに次世代のマップ配列を生成するgeneratorとして実装しています。マップは周期的境界条件(端と端がつながった状態)にも対応させてみました!
lifegame.py
以下のlifegame.py
をモジュールとしてimportして使用します。実際の使用例は下記のサンプルコードをご覧ください。進化のルールはWikipediaで紹介されている23/3
に準拠してますが、コードの中をいじって容易に変えることができます。
usage
>>> lg_map = lifegame.MapGenerator(map_init, periodic=False)
>>> lg_map.evolve()
array([...]) # 第0世代のマップ配列 (= map_init)
>>> lg_map.evolve()
array([...]) # 第1世代のマップ配列
>>> ...
-
map_init
: 初期世代のマップ配列 (list or numpy.ndarray) -
periodic
: マップに周期的境界条件を使用するかどうか (bool)
lifegame.py
lifegame.py
import numpy as np
from itertools import product
class MapGenerator(object):
def __init__(self, map_init, periodic=False):
self.map_init = np.array(map_init, np.int64)
self.periodic = periodic
self.generator = self.map_evolver()
def map_evolver(self):
map_now = self.map_init
map_shape = self.map_init.shape
while True:
map_next = np.zeros_like(map_now, np.int64)
for (i,j) in product(range(map_shape[0]), range(map_shape[1])):
islive = bool(map_now[i,j])
nlive = self.num_live_neighbours(map_now, i, j)
# write the rules of lifegame here!
# ........................................
if nlive == 2:
if islive: map_next[i,j] = 1
elif nlive == 3:
map_next[i,j] = 1
# ........................................
yield map_now
map_now = map_next
def num_live_neighbours(self, map_now, i, j):
if self.periodic:
neighbours = np.roll(np.roll(map_now, -i+1, 0), -j+1, 1)[:3,:3]
else:
sl_i = slice(0,i+2) if i == 0 else slice(i-1,i+2)
sl_j = slice(0,j+2) if j == 0 else slice(j-1,j+2)
neighbours = map_now[sl_i,sl_j]
return neighbours.sum() - map_now[i,j]
def evolve(self):
return self.generator.next()
matplotlibを使ったサンプルコード
以下ではパルサーと呼ばれる振動子の一種が、15x15のマップ上でエンドレスに進化するサンプルコードです。
あくまでサンプルなので、配列の計算速度よりもmatplotlibの描画効率でリミットされていると思われます…
サンプルコード
pulsar.py
import time
import numpy as np
import matplotlib.pyplot as plt
import lifegame
pulsar = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
lg_map = lifegame.MapGenerator(map_init=pulsar, periodic=False)
plt.ion()
while True:
plt.pcolormesh(lg_map.evolve())
plt.xlim([0, pulsar.shape[1]])
plt.ylim([0, pulsar.shape[0]])
plt.draw()
plt.clf()
time.sleep(0.05)
描画の様子
今後の予定?
現時点では初期世代のマップ配列をテキストで予め用意する必要があるので、matplotlib.widgets
を使って画面上からマップ配列をポチポチと入力できるようにもしたいなと思います。遊びなのでいつ作るかは未定ですが(笑)