Pythonでライフゲーム Gifでアニメ化
ライフゲームとは
ライフゲームとは、生物の栄枯盛衰をシミュレートするモデルです。
まずはどのようなものか、完成品を見てみましょう。
黄色いマスが生物がいるところ、紫の部分はなにもないところです。
シミュレートしてみると、黄色い部分がウヨウヨと動いていることがわかります。
移動したり、その場でとどまったり、特徴的な模様を創り出す場所もあります。
ライフゲームでは、生物は次のようなルールで動いています。
あるマスに生物がいるとき
→もしそのマスの周囲に生物が2・3匹居れば生存
→1匹なら死亡(過疎)
→4匹以上でも死亡(過密)
あるマスに生物がいないとき
→もしそのマスの周囲に生物が2匹以下か4匹以上ならそのまま
→3匹いればそのマスに生物が誕生
このような単純なルールに従っているにも関わらず、全体としては非常に複雑な動きが見られます。
このような、全体の動きが個々の単純な動きにとどまらないようなシステムを「複雑系」と呼びます。
プログラム
Pythonを使ってライフゲームを作っていきます。
ライフゲームを作るに当たり、次のような部品が必要になります。
①シミュレートするフィールドを作る
②シミュレーションを行う
③結果を保存し、gifにする
さて、まずは全体がどのようになっているのか見てみましょう。
#モジュール類 from random import randint import matplotlib.pyplot as plt import numpy as np import os from PIL import Image #シミュレーション結果の出力先 di = "./" pics = di + "pictures/" gif = di + "simulation_results/" #のフォルダ作成 try: os.mkdir(pics) os.mkdir(gif) except: pass #結果を図に起こし、保存 def make_fig(world,t): picsfname = pics + str(t) + ".png" pictitle = "Generation No." + str(t) plt.figure() plt.title(pictitle) plt.imshow(world) plt.savefig(picsfname) plt.close() #自分の周囲8マスを探し、生物の有無を確かめる def serch(world,x,y): #カウンタ c = 0 #探索 for i in range(x-1,x+2): for j in range(y-1,y+2): c += world[i][j] c -= world[x][y] #周囲に3匹入れば生存、2匹ならそのまま、それ以外は死亡 if c == 3: return 1 elif c == 2: return world[x][y] else: return 0 #パラメータ入力 d = int(input("枠の大きさ")) n = int(input("何世代?")) world = [] #フィールドの生成 #生物の初期位置は乱数で決める for i in range(d): tmp = [] for j in range(d): if randint(0,100)%7 == 0: tmp.append(1) else: tmp.append(0) world.append(tmp) #初期状態を記録 make_fig(world,0) #メインループ for t in range(1,n): newworld = [] for i in range(d): tmp = [] for j in range(d): tmp.append(0) newworld.append(tmp) for i in range(1,d-1): for j in range(1,d-1): newworld[i][j] = serch(world,i,j) for i in range(d): for j in range(d): world[i][j] = newworld[i][j] make_fig(world,t) #./picturesに保存した図を繋ぎ合わせてgifにする frames = [] giffname = gif + "simulation.gif" #画像の読み込み for i in range(n): picname = pics + str(i) + ".png" new_frame = Image.open(picname) frames.append(new_frame) #gif化 frames[0].save(giffname, format='GIF', append_images=frames[1:], save_all=True, duration=400, loop=0) print("finished")
モジュール類
from random import randint import matplotlib.pyplot as plt import os from PIL import Image
乱数生成のためのrandom、
図表作成のためのmatplotlib、
ディレクトリ操作のためのos、
gifを作るためのPILをインポートします。
①シミュレートするフィールドを作る
まずどのようなフィールドを作るのか標準入力から受け取ります。
パラメータは枠の大きさと何世代シミュレートするのかです。
#パラメータ入力 d = int(input("枠の大きさ")) n = int(input("何世代?")) world = []
フィールドを生成します。
初期状態の生物の分布は乱数を使って適当に定めます。
乱数を生物の分布を示す0/1に落とし込むため、
横枠の長さ分だけ乱数を発生させ、
次にその乱数が7の倍数なら1を
それ以外なら0を一時変数tmpに代入し、
フィールドを示すリストworldにまとめて格納します。
#フィールドの生成 #生物の初期位置は乱数で決める for i in range(d): tmp = [] for j in range(d): if randint(0,100)%7 == 0: tmp.append(1) else: tmp.append(0) world.append(tmp) 後ほど作る、状態記録のための関数make_figで初期状態を記録します。 #初期状態を記録 make_fig(world,0)
最後にシミュレーション結果の出力もここで設定しておきましょう。
try文で./pictures/というディレクトリがあればそこに画像を保存し、なければ作成します。シミュレーション結果は./simulation_results/に保存します。
#シミュレーション結果の出力先 di = "./" pics = di + "pictures/" gif = di + "simulation_results/" #のフォルダ作成 try: os.mkdir(pics) os.mkdir(gif) except: pass
②シミュレーションを行う
ライフゲームのキモはここです。
まずは
あるマスに生物がいるとき
→もしそのマスの周囲に生物が2匹居れば生存
→3匹ならそのまま
→1匹なら死亡
→4匹以上でも死亡
あるマスに生物がいないとき
→もしそのマスの周囲に生物が2匹以下か4匹以上ならそのまま
→3匹いればそのマスに生物が誕生
というルールを実現するためにマスの周囲を確認し、そのマスの状態をどう遷移させるのか定める関数を作ります。
#自分の周囲8マスを探し、生物の有無を確かめる def serch(world,x,y): #カウンタ c = 0 #探索 forの範囲をこのように定めることで、角に来ても #枠外を探索することがない for i in range(x-1,x+2): for j in range(y-1,y+2): c += world[i][j] #自分自身は探索しない c -= world[x][y] #周囲に3匹入れば生存、2匹ならそのまま、それ以外は死亡 if c == 3: return 1 elif c == 2: return world[x][y] else: return 0
あとはこのルールを全マスに対し、設定した世代だけ回せば完了です。
一世代ごとに状態をmake_figで記録します。
#メインループ for t in range(1,n): newworld = [] for i in range(d): tmp = [] for j in range(d): tmp.append(0) newworld.append(tmp) for i in range(1,d-1): for j in range(1,d-1): newworld[i][j] = serch(world,i,j) for i in range(d): for j in range(d): world[i][j] = newworld[i][j] make_fig(world,t)
③結果を保存し、gifにする
まず結果をmatplotlibで図に起こします。
def make_fig(world,t): #画像の名前を設定します。 #例えば、./pictures/1.png のような名前で保存されます。 #./pictures/はディレクトリ名なので自動的にこの画像は #./pictures/に入ることになります。 #このように連番で名前をつけるのは、分かりやすくする以上に、 #結果が世代でソートされた状態で保存されるため、 #後にgif化しやすいからです。 picsfname = pics + str(t) + ".png" pictitle = "Generation No." + str(t) #枠を生成 plt.figure() #図の上に表示するタイトル plt.title(pictitle) #2次元平面上にプロット plt.imshow(world) #保存 plt.savefig(picsfname) plt.close()
最後に、./pictures/にある画像をまとめて拾ってgif化します。
#画像の在り処を格納するframesリストを作成し frames = [] #シミュレーション結果は./simulation_results/simulation.gif #で保存します。 giffname = gif + "simulation.gif" #for文で画像を次々に読み込みます for i in range(n): picname = pics + str(i) + ".png" new_frame = Image.open(picname) frames.append(new_frame) #それらをまとめてgif化します。 frames[0].save(giffname, format='GIF', append_images=frames[1:], save_all=True, duration=400, loop=0) #プログラムが終了したことを知らせる print("finished")
ここまでを実行すれば、最初のようなgifアニメーションを得ることができます。
宣伝
ココナラでフリーのライターをやっています。
これまでに280件以上の受注実績があります。
大学入試の小論文、学生・社会人の方のレポートの作成・添削のご相談はこちらまで!
coconala.com