JavaScriptでLife-like Cellular Automataを実装
この記事ではライフゲーム(Conway's Game of Life)の拡張の一種である、Life-like Cellular AutomataのJavaScript実装を紹介します。
See the Pen
LifeLikeCellularAutomaton B35678/S5678 by Imai (@imai1)
on CodePen.
Conwayのオリジナルのルールでは、死滅状態のセルは隣接セルの生存数が3のとき誕生し、生存状態のセルは隣接の生存数が2か3のときに生存し続けます。このルールをB3/S23と呼んだりもします。BirthのBとSurvivalのSです。
この記法に則れば、誕生/生存の隣接セル数の条件を変更した別のルールも簡単に表せます。例えば上記のCodePenはB35678/S5678を実装したもので、通称Diamoebaと呼ばれています。
コード中の最後の方の次の部分が更新ルールを表しています。
const rule = new UpdateRule([ [0, 0, 0, 1, 0, 1, 1, 1, 1], // B [0, 0, 0, 0, 0, 1, 1, 1, 1], // S ]);
CodePen上でこの部分を編集して再実行すれば別のルールで遊ぶことができますのでぜひ試してみてください。
簡単なコード解説
全体構成の概要
まず前提として、冒頭のコードは、前の記事で紹介したオリジナルのライフゲームのコードとほぼ同じです。
OriginalRule
クラスをUpdateRule
クラスに書き換え、CanvasView
に色を変更するパラメータを追加した以外、前の記事のコードと冒頭のコードで実質的な変更はありません。何番煎じかわからないため設計にこだわって作ったという旨を前の記事に書きましたが、うまくいったのではないかと思います。
この記事よりは少し細かくコードについて説明しています。ご興味があればそちらも参照いただければ嬉しいです。
今回のコードについては、LifeLikeCellularAutomaton
が全体を管理するクラスです。このクラスのインスタンスに、盤面状態・更新ルール・ビューに対応するBoard, UpdateRule, CanvasViewクラスのインスタンスを合成することで全体を構成します。
Board
クラスは、ルールやビューに依存しない、盤面状態の二次元配列を管理します。与えられた座標のセルの状態をget/setしたり、そのセルの隣接セルの生存数を数えたりする役割です。このクラスを変更すると、盤面の端を接続したトーラス状のオートマトンなども実装できます。
CanvasView
クラスは、Board
クラスを受け取って、与えられたIDのcanvas
タグに盤面状態を描画するクラスです。Board
のみに依存し、UpdateRule
やLifeLikeCellularAutomaton
からは独立しています。同じインターフェースで、div
やtable
などのDOM要素で盤面を表現するクラスに置き換えることも可能です。
UpdateRuleクラス
/** * セルの次状態を計算するクラス。 * 現在の状態と隣接セルの状態から次状態をルックアップテーブルで参照するのみ。 */ class UpdateRule { constructor(nextValueLookupTable) { this.nextValueLookupTable = Object.freeze(nextValueLookupTable); } nextValue(currentValue, sumNeighbors) { return this.nextValueLookupTable[currentValue][sumNeighbors]; } }
UpdateRule
クラスは、nextValue(currentValue, sumNeighbors)
関数で、セルの次状態を計算するクラスです。
JSDocの通り、コンストラクタのパラメータで与えられたルックアップテーブルをnextValue
関数から参照するだけの極めてシンプルなクラスです。このコンストラクタにB3/S23やB35678/S5678などを表現する2x9の整数配列を与えることで、個別のルールを表現できます。