前回、「Pythonでライフゲーム(Conway’s Game of Life)を作って遊んでみた」にてライフゲームを実装しました。
最後の考察で下記コメントを記載しました。
考えているのは、男女のセルを作成し両方が存在する場合に新しい生命が生まれるなどいう変更を加えてみるのも面白そうかなと思っています。
あとは寿命や食事という概念を追加したり、グリッドもただのグリッドではなく、水や森といったフィールドをランダムで発生させるのも楽しそうかなと思います。
ということで早速ですが、セルに性別の概念を追加してみました。(このゲーム気に入ってます 笑)
numpyで配列を作成する時に初期値として男性用の数値と女性用の数値を割り当てる
男性=255、女性=126としていますが、数値の大きさに意味はありません。
単純にそのまま可視化した時に、近い数値だと色が変わらなかったので半分くらいの数値にしただけになります。
プロットする時に指定した色に変更することも今後やっていこうと思います。
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# 変数宣言
N=9 # 9 x 9のグリッドを構築
MALE = 255 # 生物(male)
FEMALE = 126 # 生物(female)
NONE = 0 # 何もなし
vals = [MALE,FEMALE,NONE]
# N x Nの配列を作成。男女の出現をそれぞれ0.1に設定
grid = np.random.choice(vals, N*N, p=[0.1,0.1,0.8]).reshape(N, N)
Out[0]
array([[255, 0, 0, 0, 0, 126, 255, 0, 0], [ 0, 0, 0, 255, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 126, 0], [255, 255, 126, 0, 255, 0, 255, 255, 0], [126, 0, 255, 0, 0, 0, 0, 0, 126], [ 0, 0, 126, 0, 0, 0, 255, 0, 0], [ 0, 255, 0, 0, 0, 126, 0, 0, 0], [ 0, 0, 255, 255, 0, 0, 0, 0, 255], [126, 0, 0, 0, 126, 0, 0, 0, 0]])
255と0の他に126も出現していることが分かります。
可視化
fig, ax = plt.subplots()
img = ax.imshow(grid)
Out[0]
黄色が男子のセル、エメラルドっぽい青色が女子のセルを表しています。
ライフゲームのロジックに性別の概念を追加する
元のルールをあまり壊さないようにしています。
新しい生命が生まれる条件が「周囲にちょうど3つセルがあること」になっているので、周囲に自分自身とは反対の性別のセルがある場合に生命を誕生させるという風にしました。
また、生まれるセルも男女のどちらかになるようにランダム性も持たせています。
ライフゲーム更新のロジック(男女の概念を追加)
# https://stackoverflow.com/questions/1692388/python-list-of-dict-if-exists-increment-a-dict-value-if-not-append-a-new-dic
from collections import defaultdict
import random
newGrid = grid.copy()
# 各セルの情報を取得し、gridを更新
for i in range(N):
for j in range(N):
cell_info = defaultdict(int)
# 今後の拡張性を考えて、各要素の出現回数を保存
cell_info[grid[i, (j-1)%N]] += 1 # (0,8) 左のセル
cell_info[grid[i, (j+1)%N]] += 1 # (0,1) 右のセル
cell_info[grid[(i-1)%N, j]] += 1 # (8,0) 上のセル
cell_info[grid[(i+1)%N, j]] += 1 # (1,0) 下のセル
cell_info[grid[(i-1)%N, (j-1)%N]] += 1 # (8,8) 左上のセル
cell_info[grid[(i-1)%N, (j+1)%N]] += 1 # (8,1) 右上のセル
cell_info[grid[(i+1)%N, (j-1)%N]] += 1 # (1,8) 左下のセル
cell_info[grid[(i+1)%N, (j+1)%N]] += 1 # (1,1) 右下のセル
print("(%d,%d)のセルの値は%d: 周辺の%sの数は%d、周辺の%sの数は%d" % (i, j,grid[i,j],MALE,cell_info[MALE],FEMALE,cell_info[FEMALE]))
# reproductionのルールに性別の概念を追加。同一の性別のみだと生命が誕生しないことにする。
ORGANISMS = cell_info[MALE] + cell_info[FEMALE]
if grid[i, j] != NONE:
# 1. underpopulation and 3. overpopulation
if (ORGANISMS < 2) or (ORGANISMS > 3):
newGrid[i, j] = NONE
else:
# two or three live neighbours
# do nothing and remains on (to the 2. next generation)
continue
else:
# 4. reproduction
# ランダムに男女の誕生を選択。低い確率で生物が生まれないようにした。
REPRODUCE = random.choices(vals, weights=[10,10,1])[0]
if ORGANISMS == 3:
# 自分自身が男性、周りに女性がいる場合
if grid[i, j] == MALE and cell_info[FEMALE] > 0:
newGrid[i, j] = REPRODUCE
# 自分自身が女性、周りに男性がいる場合
elif grid[i, j] == FEMALE and cell_info[MALE] > 0:
newGrid[i, j] = REPRODUCE
else:
newGrid[i, j] = NONE
Out[0]
(0,0)のセルの値は255: 周辺の255の数は0、周辺の126の数は1 (0,1)のセルの値は0: 周辺の255の数は1、周辺の126の数は1 (0,2)のセルの値は0: 周辺の255の数は1、周辺の126の数は0 ・・・省略・・・ (8,6)のセルの値は0: 周辺の255の数は1、周辺の126の数は1 (8,7)のセルの値は0: 周辺の255の数は2、周辺の126の数は0 (8,8)のセルの値は0: 周辺の255の数は2、周辺の126の数は1
更新後のグリッドを確認
newGrid
Out[0]
array([[ 0, 0, 0, 0, 0, 126, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0, 126, 0], [255, 0, 126, 0, 0, 0, 255, 255, 0], [126, 0, 255, 0, 0, 0, 0, 0, 126], [ 0, 0, 126, 0, 0, 0, 0, 0, 0], [ 0, 255, 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 255, 255, 0, 0, 0, 0, 0], [126, 0, 0, 0, 126, 0, 0, 0, 0]])
新グリッドの描画
fig, ax = plt.subplots()
img = ax.imshow(newGrid)
Out[0]
ライフゲームを動かしてみる
ライフゲームを動かす
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# 変数宣言
N=9 # 9 x 9のグリッドを構築
MALE = 255 # 生物(male)
FEMALE = 126 # 生物(female)
NONE = 0 # 何もなし
vals = [MALE,FEMALE,NONE]
# N x Nの配列を作成。ON:OFFの割合を2:8で初期化
grid = np.random.choice(vals, N*N, p=[0.1,0.1,0.8]).reshape(N, N)
def update(frameNum, img, grid, N):
# https://stackoverflow.com/questions/1692388/python-list-of-dict-if-exists-increment-a-dict-value-if-not-append-a-new-dic
from collections import defaultdict
newGrid = grid.copy()
# 各セルの情報を取得し、gridを更新
for i in range(N):
for j in range(N):
cell_info = defaultdict(int)
# 今後の拡張性を考えて、各要素の出現回数を保存
cell_info[grid[i, (j-1)%N]] += 1 # (0,8) 左のセル
cell_info[grid[i, (j+1)%N]] += 1 # (0,1) 右のセル
cell_info[grid[(i-1)%N, j]] += 1 # (8,0) 上のセル
cell_info[grid[(i+1)%N, j]] += 1 # (1,0) 下のセル
cell_info[grid[(i-1)%N, (j-1)%N]] += 1 # (8,8) 左上のセル
cell_info[grid[(i-1)%N, (j+1)%N]] += 1 # (8,1) 右上のセル
cell_info[grid[(i+1)%N, (j-1)%N]] += 1 # (1,8) 左下のセル
cell_info[grid[(i+1)%N, (j+1)%N]] += 1 # (1,1) 右下のセル
# reproductionのルールに性別の概念を追加。同一の性別のみだと生命が誕生しないことにする。
ORGANISMS = cell_info[MALE] + cell_info[FEMALE]
if grid[i, j] != NONE:
# 1. underpopulation and 3. overpopulation
if (ORGANISMS < 2) or (ORGANISMS > 3):
newGrid[i, j] = NONE
else:
# two or three live neighbours
# do nothing and remains on (to the 2. next generation)
continue
else:
# 4. reproduction
# ランダムに男女の誕生を選択。低い確率で生物が生まれないようにした。
REPRODUCE = random.choices(vals, weights=[10,10,1])[0]
if ORGANISMS == 3:
# 自分自身が男性、周りに女性がいる場合
if grid[i, j] == MALE and cell_info[FEMALE] > 0:
newGrid[i, j] = REPRODUCE
# 自分自身が女性、周りに男性がいる場合
elif grid[i, j] == FEMALE and cell_info[MALE] > 0:
newGrid[i, j] = REPRODUCE
else:
newGrid[i, j] = NONE
# update data
img.set_data(newGrid)
# Shallow copies refer to below link
# https://stackoverflow.com/questions/6167238/what-does-mean
grid[:] = newGrid[:]
return img
%matplotlib notebook
# set up animation
fig, ax = plt.subplots()
img = ax.imshow(grid)
ani = animation.FuncAnimation(fig, update, fargs=(img, grid, N),frames = 10,interval=500,save_count=50)
plt.show()
Out[0]
何となく全ての生物がいなくなる状態になることが減った気がします。
まとめ
今までのライフゲームのロジックに男女の概念を追加実装しました。
次は寿命の概念を追加したいですね。