サイトリニューアルしました。

17-3. ライフゲーム(正方形、重み付き)

やること

ライフゲームには様々なバリエーションがあります。今回は重み付きのルールを試してみましょう。

参考文献

こちらの論文を参考にさせていただきました。

http://www.kurims.kyoto-u.ac.jp/~kyodo/kokyuroku/contents/pdf/0853-10.pdf

基本ルールと重み付きルール

17-1で用いた基本ルールでは、各セルは周りの8マス(=ムーア近傍)にある生存セルの影響を一様に受けていました。

ここで、隣り合う4マス(=ノイマン近傍)の影響を2倍受けることにするのが今回のルールです。

生存ルールは、まずは23/45ルールとします。

次のステップでの生存条件
生存セル隣接する8セルのうち生存セルが2、3個
死セル隣接する8セルのうち生存セルが4、5個

実行環境

どちらかを用いますが、WinPythonをおすすめします。WinPythonでは図が紙芝居のようにパラパラ流れて見やすいですが、Colabではスクロールする必要があり見づらいです。

Colabにも対応しました!(推奨)

WinPython - Browse /WinPython_3.5/3.5.3.1 at SourceForge.net
Portable Scientific Python 2/3 32/64bit Distribution for Windows
Google Colaboratory

ソースコード

基本的には17-1の記事のソースコードのうち、周囲のマスを足し込む部分と、セルの生存条件の部分を書き換えました。

↓WinPythonコード

import numpy as np
import numpy.random as nr
import matplotlib.pyplot as plt

#============================
#初期状態の設定
#============================

#高さ、幅
h, w = 16, 16

#任意の状態を用意
state = np.array([[0,1,1,0],
                  [1,0,0,1],
                  [0,0,0,0],
                  [0,1,1,0]])

#フィールドのどこに置くか(左上点を指定)
p = (12, 6)

#終了ステップ数
max_step = 30

#============================
#メイン処理
#============================

#フィールドの生成
f = np.zeros((h, w), dtype=bool)

#任意の状態を置く
f[p[0]:p[0]+len(state), p[1]:p[1]+len(state[0])] = state

#初期状態の表示
plt.imshow(f, cmap='inferno')
#plt.savefig('save/{}.png'.format(0), bbox_inches='tight', pad_inches=0)
plt.show(), print()

#状態の更新
for i in range(1, max_step + 1):
    
    #周囲の生存マス数を記録するための配列
    mask = np.zeros((h, w))
    
    #周囲の生存マスを足し込む
    mask[1:, :] += f[:-1, :] * 2 #上 #ここ
    mask[:-1, :] += f[1:, :] * 2 #下 #ここ
    mask[:, 1:] += f[:, :-1] * 2 #左 #ここ
    mask[:, :-1] += f[:, 1:] * 2 #右 #ここ
    mask[1:, 1:] += f[:-1, :-1] #左上
    mask[1:, :-1] += f[:-1, 1:] #右上
    mask[:-1, 1:] += f[1:, :-1] #左下
    mask[:-1, :-1] += f[1:, 1:] #右下
    
    #未来のフィールド(すべて死状態)
    future = np.zeros((h, w), dtype=bool)
    
    #生きているマスが生きる条件(=生存)
    future[mask*f==2] = 1
    future[mask*f==3] = 1
    #死んでいるマスが生きる条件(=誕生)
    future[mask*~f==4] = 1
    future[mask*~f==5] = 1
    
    #フィールドの更新(浅いコピーに注意)
    f = future
    
    #表示
    plt.imshow(f, cmap='inferno')
    #plt.savefig('save/{}.png'.format(i), bbox_inches='tight', pad_inches=0)
    plt.show(), print()

↓Colabコード

import numpy as np
import numpy.random as nr
import matplotlib.pyplot as plt
from matplotlib import animation, rc

#============================
#初期状態の設定
#============================

#高さ、幅
h, w = 16, 16

#任意の状態を用意
state = np.array([[0,1,1,0],
                  [1,0,0,1],
                  [0,0,0,0],
                  [0,1,1,0]])

#フィールドのどこに置くか(左上点を指定)
p = (12, 6)

#終了ステップ数
max_step = 30

#============================
#メイン処理
#============================

#フィールドの生成
f = np.zeros((h, w), dtype=bool)

#任意の状態を置く
f[p[0]:p[0]+len(state), p[1]:p[1]+len(state[0])] = state

#画像をスタックする配列の準備
fig = plt.figure()
#fig = plt.figure(figsize=(10, 10))
ims = []

#初期状態の表示(スタック)
im = plt.imshow(f, cmap='inferno')
ims.append([im])

#状態の更新
for i in range(1, max_step + 1):
    
    #周囲の生存マス数を記録するための配列
    mask = np.zeros((h, w))
    
    #周囲の生存マスを足し込む
    mask[1:, :] += f[:-1, :] * 2 #上 #ここ
    mask[:-1, :] += f[1:, :] * 2 #下 #ここ
    mask[:, 1:] += f[:, :-1] * 2 #左 #ここ
    mask[:, :-1] += f[:, 1:] * 2 #右 #ここ
    mask[1:, 1:] += f[:-1, :-1] #左上
    mask[1:, :-1] += f[:-1, 1:] #右上
    mask[:-1, 1:] += f[1:, :-1] #左下
    mask[:-1, :-1] += f[1:, 1:] #右下
    
    #未来のフィールド(すべて死状態)
    future = np.zeros((h, w), dtype=bool)
    
    #生きているマスが生きる条件(=生存)
    future[mask*f==2] = 1
    future[mask*f==3] = 1
    #死んでいるマスが生きる条件(=誕生)
    future[mask*~f==4] = 1
    future[mask*~f==5] = 1
    
    #フィールドの更新(浅いコピーに注意)
    f = future
    
    #表示(スタック)
    im = plt.imshow(f, cmap='inferno')
    ims.append([im])

#アニメーション表示の準備
print('making animation......')
print('len(ims) = ' + str(len(ims)))
ani = animation.ArtistAnimation(fig, ims, interval=50, blit=True)

#ここにアニメを表示する場合はこちらを有効に
rc('animation', html='jshtml')
ani

#.gifや.mp4を保存する場合はこちらを有効に
#ani.save('aaa.gif', writer='imagemagick',fps=40)
#ani.save('bbb.mp4', writer="ffmpeg")

グライダーを実行してみる

ソースコードをそのまま実行すると、独特のグライダーが飛び始めます。

26周期振動子

#============================
#初期状態の設定
#============================

#高さ、幅
h, w = 10, 11

#任意の状態を用意
state = np.array([[0,1,1],
                  [1,0,0],
                  [0,0,1],
                  [1,1,0]])

#フィールドのどこに置くか(左上点を指定)
p = (3, 4)

#終了ステップ数
max_step = 30

他のルールを試してみる

重み付けはそのままで、3456/4ルールに変えてみます。コードは適切に書き換えてください。

次のステップでの生存条件
生存セル隣接する8セルのうち生存セルが3、4、5、6個
死セル隣接する8セルのうち生存セルが4個
#============================
#初期状態の設定
#============================

#高さ、幅
h, w = 10, 10

#任意の状態を用意
state = np.array([[1,1,1,1],
                  [1,0,0,1]])

#フィールドのどこに置くか(左上点を指定)
p = (8, 3)

#終了ステップ数
max_step = 30

シュシュポッポ列車

#============================
#初期状態の設定
#============================

#高さ、幅
h, w = 12, 40

#任意の状態を用意
state = np.array([[0,1,0,0],
                  [0,1,1,0],
                  [1,0,0,1],
                  [1,0,0,1],
                  [0,1,1,0],
                  [0,1,0,0]])

#フィールドのどこに置くか(左上点を指定)
p = (3, 0)

#終了ステップ数
max_step = 110

まとめ

楽しい(中毒)。皆さんも実装できたらぜひSlackで報告してください。または、Twitterでこの記事のURLを添付してつぶやいていただけたら嬉しいです。

タイトルとURLをコピーしました