11-03. ライフゲーム
03. ライフゲームのプログラムを組んでみてください.¶
03-01. ライフゲーム遷移ルール¶
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from IPython.display import HTML以下のような関数を定義してみよう.
def 遷移ルール(3x3の配列(注目するセルとその8近傍)):
近傍の“生”(1)のセルの数を計算する(np.sumを使ってみよう)
近傍の“生”のセルがちょうど2つならば:
次世代の(中心の)セルの状態は更新なし
(2ではなく)近傍の“生”のセルがちょうど3つならば:
次世代の(中心の)セルの状態は“生”
それ以外の場合(過疎,過密):
次世代の(中心の)セルの状態は“死”
return 次世代の(中心の)セルの状態Solution to Exercise 1
# 03-01. ライフゲーム遷移ルールの定義
def rule(cells):
"""Conway's Game of Lifeの遷移ルール
入力された3x3の配列に基づき次世代の状態を返す.
* 誕生:8近傍中ちょうど3つが1ならば次のステップで1
* 維持:8近傍中ちょうど2つが1ならば次のステップで更新なし(1ならば1,0ならば0)
* 過疎:8近傍中1が1つ以下ならば次のステップで0
* 過密:8近傍中1が4つ以上ならば次のステップで0
Args:
cells: 3x3の配列.中心(cells[1,1])とその8近傍.
Returns:
new_cell: 次世代の中心セルの状態.
"""
num_neighbors = np.sum(cells) - cells[1, 1]
if num_neighbors == 2:
new_cell = np.copy(cells[1, 1])
elif num_neighbors == 3:
new_cell = 1
else:
new_cell = 0
return new_cell03-02. ライフゲームの実行¶
作成方針を以下に示す.
# 03-02. ライフゲームの実行
# 初期条件の設定
* 場のサイズを設定(縦,横にそれぞれ何個セルが配置されるか?)
* 何世代目まで計算するか?
* 場を記録するリストの準備
* 場の初期値の設定(例.ランダム,特定パタンの配置など)
for 世代:
# 状態遷移
次世代の場を記録する配列の用意
for i in 場のサイズ(縦):
for j in 場のサイズ(横):
# 境界条件等による分岐
# 全部で9通り(メイン+境界条件)
もしi==0ならば:
もしj==0ならば:
次世代の場[i,j] = 遷移ルール(場[[-1,0,1],:][:,[-1,0,1]])
(j==0でなく)もし0<j<場のサイズ(横)-1ならば:
次世代の場[i,j] = 遷移ルール(場[[-1,0,1],:][:,[j-1,j,j+1]])
(j<場のサイズ(横)-1でなく)もしj==場のサイズ(横)-1ならば:
…
(i==0でなく)もし0<i<場のサイズ(縦)-1ならば:
…
(i<場のサイズ(縦)-1でなく)もしi==場のサイズ(縦)-1ならば:
…
場の更新(次世代を現世代にコピー, np.copyを使ってみよう)
場の記録(リストへ追加)場の更新をおこなう関数を定義してみよう.
Solution to Exercise 2
def update(field):
"""ライフゲームの更新
`rule()`関数に従い,場の更新をおこなう.周期境界条件を仮定している.
Args:
field: 現在の場の配列
Returns:
new_field: 次世代の場の配列
"""
n_i, n_j = field.shape
new_field = np.zeros((n_i, n_j), dtype=field.dtype)
for i in range(n_i):
for j in range(n_j):
if i == 0:
if j == 0:
# 境界条件処理 i=0,j=0
new_field[i, j] = rule(field[[-1, 0, 1], :][:, [-1, 0, 1]])
elif 0 < j < n_j - 1:
# 境界条件処理 i=0,0<j<n_j-1
new_field[i, j] = rule(field[[-1, 0, 1], :][:, [j - 1, j, j + 1]])
elif j == n_j - 1:
# 境界条件処理 i=0,j=n_j-1
new_field[i, j] = rule(
field[[-1, 0, 1], :][:, [n_j - 2, n_j - 1, 0]]
)
elif 0 < i < n_i - 1:
if j == 0:
# 境界条件処理 1<i<n_i-1,j=0
new_field[i, j] = rule(field[[i - 1, i, i + 1], :][:, [-1, 0, 1]])
elif 0 < j < n_j - 1:
# メイン (1<i<n_i-1,1<j<n_j-1)
new_field[i, j] = rule(
field[[i - 1, i, i + 1], :][:, [j - 1, j, j + 1]]
)
elif j == n_j - 1:
# 境界条件処理 1<i<n_i-1,j=n_j-1
new_field[i, j] = rule(
field[[i - 1, i, i + 1],][:, [n_j - 2, n_j - 1, 0]]
)
elif i == n_i - 1:
if j == 0:
# 境界条件処理 i=n_i,j=0
new_field[i, j] = rule(field[[n_i - 2, n_i - 1, 0],][:, [-1, 0, 1]])
elif 0 < j < n_j - 1:
# 境界条件処理 i=n_i-1,1<j<n_j-1
new_field[i, j] = rule(
field[[n_i - 2, n_i - 1, 0],][:, [j - 1, j, j + 1]]
)
elif j == n_j - 1:
# 境界条件処理 i=n_i-1,j=n_j-1
new_field[i, j] = rule(
field[[n_i - 2, n_i - 1, 0],][:, [n_j - 2, n_j - 1, 0]]
)
return new_field定義したupdateを使ってシミュレーションをしてみよう.
以下では例としてグライダー銃のパターンを使ってシミュレーションする.
# グライダー銃
ptn_glidergun = 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, 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, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 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, 0, 0, 1, 0, 0, 0, 0, 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, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
], dtype=int)# 03-02. ライフゲームの実行
n_i = 50
n_j = 50
steps = 50
field_list = []
field = np.zeros((n_i,n_j),dtype=int)
field[5:5+ptn_glidergun.shape[0],5:5+ptn_glidergun.shape[1]] = np.copy(ptn_glidergun)
# field=np.random.randint(2,size=field_size,dtype=int)
field_list.append(field)
for t in range(1,steps+1):
if t%10 == 0:
print("Steps: ", t)
# -- 状態遷移 --
field_tmp = update(field)
# 情報の更新
field = np.copy(field_tmp)
field_list.append(field)Steps: 10
Steps: 20
Steps: 30
Steps: 40
Steps: 50
03-03. セルオートマトンの可視化¶
# 03-03. セルオートマトンの可視化
plt.figure(figsize=(8, 8))
plt.matshow(field_list[50], fignum=1, cmap="binary")
03-04. セルオートマトンの可視化 アニメーション¶
# 03-04. セルオートマトンの可視化 アニメーション
from matplotlib import animation, rc
from IPython.display import HTML
fig, ax = plt.subplots(dpi=150)
plt.close()
artists = []
for i in range(len(field_list)):
im = ax.matshow(field_list[i], interpolation="none", cmap="binary")
ax.set_aspect("equal")
artists.append([im])
# アニメーションへの変換
ani = animation.ArtistAnimation(fig, artists, interval=100)
# アニメーションの表示(JavaScriptとして埋め込み)
rc("animation", html="jshtml")
aniLoading...
ライフゲームのパターンの例¶
LifeWikiにて,多数のパターンを見つけることができる.
また,パターンをテキストしてコピーしたり,ファイルとしてダウンロードできる.
以下にはいくつかの例を示す.
グライダー¶
# グライダー
ptn_glider = np.array([[1, 1, 1], [1, 0, 0], [0, 1, 0]], dtype=int)回転花火¶
# 回転花火
ptn_pinwheel = np.array(
[
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0],
[1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1],
[0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0],
],
dtype=int,
)グライダー銃¶
# グライダー銃
ptn_glidergun = 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, 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, 1, 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, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 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, 0, 0, 1, 0, 0, 0, 0, 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, 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, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
], dtype=int)パン屋¶
# パン屋
ptn_baker = np.array([
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
], dtype=int)