ライフゲームのルール:要求仕様
ライフゲームは、生命が誕生し、次々と進化を遂げ、やがて自然淘汰されていく過程を表現します。ルールは簡単。過疎でも過密でもその生命は生き残れません。

死滅したセルの周囲に3つのセルが生存していると、次世代では新たに生命が誕生します。

生存するセルの周囲に2~3のセルが生存していると、次世代でもその生命は存続します。

上記以外の場合、次世代でその生命は死滅します。
ライフゲームはそのルール自体も進化しており、いくつかの変種が存在します。変種ルールは「23/3」などと表記して、その違いを区別します。左側の数「2」と「3」は「生存」に必要な数を、右側の数「3」は「誕生」に必要な数をそれぞれ表します。例えばルール「23/36」では、元の条件に加えて、死滅した生命体の周囲に6つの生命体が生存する場合でも、次の世代で新たに生命が誕生します。
生命が存続する状態には、4つのパターン(固定型、振動型、移動型、繁殖型)に分類されます。

固定型は、その形を変えず同じ場所に留まります。振動型は、一定の周期を経て元の形に戻ります。移動型は、一定の周期を繰り返しながら移動します。繁殖型は(マス目が無限なら)増殖を繰り返します。
次に示すパターンは、タマゴ倶楽部スタッフが発見した事例(振動型)の一つで、ボタンを押すたびに面白い表情(156世代目:レッサーパンダ?)が楽しめます。

例外の隘路
先の連載第2回で紹介したように、配列の添字が範囲を超えると例外が発生します。伝統的な問題解決の手法では、この例外を捕捉するのに try 文を利用します。

Java で記述したコードの断片は、次のようになるでしょう。
int neighbors() {
int count = 0;
for (int i = y-1; i <= y+1; i++) {
for (int j = x-1; j <= x+1; j++) {
try {
if (cells[i][j])
count++;
}
catch (ArrayIndexOutOfBoundsException e) {
...
}
}
}
return count;
}
ここで着目して欲しいのは、配列の添字が範囲内にあるかを判定せずに、例外として処理していることです。理想世界のライフゲームは無限に広がりますが、現実世界の記憶容量は有限です。有限である境界を超えると、実行時に例外を生成します。そのため、伝統的な手法では、このtry文は「必要悪」とされてきました。
諸悪の根源は何処に
このプログラムは、意図したように動作します。動かないプログラムより、動くプログラムの方が望ましいのは確かです。しかし、問題がないわけではありません。例えば、「マス目の数を増やしたい」という仕様変更があったとします。ここでは、マス目の初期値 48×48 を変更するだけで、柔軟に対処できるようなコードを記述してあります。しかし、例外が発生するのを想定してtry文が必要になるという不条理には、納得がいきません。利用者は、開発者の都合に左右されたくないのです。
この不条理を避けて、見通しの良いコードを記述するにはどうすればよいでしょう。諸悪の根源は何でしょうか。伝統的な手法では、ゲーム盤を表現するのに2次元配列を利用しました。「配列」さえ利用しなければ、この問題を回避できるかもしれません。もしかすると、自分が招いた問題を、自分で解決する羽目に陥っていたのでしょうか。これは、確かめてみるだけの価値がありそうです。
