どせいたんさき。

ナスダヨー

Octave でライフゲームを作ってみた

ライフゲームの世界 - 人工知能に関する断創録 にて紹介されているライフゲーム動画を見ていたらなんだか自分も触ってみたくなったので作ってみることにした.まずは動くものが欲しかったので手っ取り早く作れそうな octave で実装した.

ライフゲームとは

ライフゲーム (Conway's Game of Life[1]) は1970年にイギリスの数学者ジョン・ホートン・コンウェイ (John Horton Conway) が考案した生命の誕生、進化、淘汰などのプロセスを簡易的なモデルで再現したシミュレーションゲームである。

ライフゲーム - Wikipedia

セルオートマトンの一種である.ここでは二次元格子状のセルを考える.セルは生きている状態と死んでいる状態のどちらかをとる.セルの状態は隣接するセルの現在の状態によって次の時間の状態が決定される.状態を決めるルールは以下の通り.

  • セルが生きている
    • 生きている隣接セル数が 2 つ以上 3 つ以下 → セルは生存
    • それ以外 → セルは死ぬ
  • セルが死んでいる
    • 生きている隣接セル数が 3 つ → セルに生命が生まれる
    • それ以外 → セルの状態はそのまま

ライフゲームをつくる

Octave は行列を簡単に扱うことができる.そこでライフゲームのセルは行列を使うことにした.ライフゲームを実行するために以下の関数を作成した.

canvas = lifegame_step(canvas)
## 行列 canvas を読み込んでライフゲームを単位時間すすめるための関数
## 返り値は更新された canvas

canvas = lifegame_play(canvas, step)
## 行列 canvas が step 数実行される様子を表示するための関数
## 返り値は step 数だけ更新された canvas

canvas = lifegame_print(canvas, step)
## 行列 canvas が step 数実行される様子を画像に出力するための関数
## 返り値は step 数だけ更新された canvas
lifegame_step

lifegame_stepライフゲームをすすめるための関数で後述の lifegame_playlifegame_print の内部で実行されている.具体的には以下のようにして定義している.

function canvas=lifegame_step(canvas,threshold=3)
  canvas = int8((canvas != 0));                    # 0 以外の値はすべて生きているとみなす
  c = @(x,y) shift(shift(canvas,x,1),y,2);         # 無名関数でエイリアスをつくる
  env = c(-1,-1)+c(0,-1)+c(1,-1)+c(-1, 0)\
	+c( 1, 0)+c(-1, 1)+c( 0, 1)+c( 1, 1);      # 周囲のセルの情報を収集
  canvas = (canvas==0 & env==threshold) \          # セルに生命が生まれる 
	   | (canvas==1 & env>1 & env<=threshold); # セルが生き残る
endfunction

どうせ 1,0 の値しか使用しないので int8() をかませてメモリを節約している.周囲のセルの情報をは shift(A,len,dim) をつかって収集している.これによって Octave が苦手なループ処理を使わずに済む.またこれによって自動的に周期的境界条件が満たされることになる.

lifegame_play

lifegame_play では imshow() を利用して gnuplot 上に行列の値を画像表示している.画像の更新にかかるコストがかなりあるので更新のスピードはあまり早くできない.行列の大きさが大きくなると画面のチラツキがより目立つようになり,さらに行列の大きさが大きくなるとメモリを食い尽くして死ぬ.

function canvas=lifegame_play(canvas, step=30, wait=100)
  for k=1:step
    imshow(!canvas);                 # 目がチカチカするので反転して表示
    drawnow("expose");               # 画像が即座に反映されるように
    usleep(wait*1e3);                # ウェイトで表示スピードを調節
    canvas = lifegame_step(canvas);  # ライフゲームを更新
  endfor
endfunction
lifegame_print

lifegame_print は画面に出力せずに gif 画像としてファイルに書き出すための関数.画像は life????.gif という連番ファイルで出力されるので ImageMagickconvert などを使用すれば簡単に gif 動画を作成することができる.

function canvas=lifegame_print(canvas, step=30)
  for k=1:step
    imwrite(logical(canvas),
            sprintf("life%04d.gif",k),"gif");  # gif 画像で出力
    canvas = lifegame_step(canvas);            # ライフゲームを更新
  endfor
endfunction

そんなわけで作成した gif 動画がこちら.