ITエンジニア勉強ブログ

自分が学んだことを共有していきます。

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上でこの部分を編集して再実行すれば別のルールで遊ぶことができますのでぜひ試してみてください。

簡単なコード解説

全体構成の概要

まず前提として、冒頭のコードは、前の記事で紹介したオリジナルのライフゲームのコードとほぼ同じです。

imai1.hatenablog.com

OriginalRuleクラスをUpdateRuleクラスに書き換え、CanvasViewに色を変更するパラメータを追加した以外、前の記事のコードと冒頭のコードで実質的な変更はありません。何番煎じかわからないため設計にこだわって作ったという旨を前の記事に書きましたが、うまくいったのではないかと思います。

この記事よりは少し細かくコードについて説明しています。ご興味があればそちらも参照いただければ嬉しいです。


今回のコードについては、LifeLikeCellularAutomatonが全体を管理するクラスです。このクラスのインスタンスに、盤面状態・更新ルール・ビューに対応するBoard, UpdateRule, CanvasViewクラスのインスタンスを合成することで全体を構成します。

Boardクラスは、ルールやビューに依存しない、盤面状態の二次元配列を管理します。与えられた座標のセルの状態をget/setしたり、そのセルの隣接セルの生存数を数えたりする役割です。このクラスを変更すると、盤面の端を接続したトーラス状のオートマトンなども実装できます。

CanvasViewクラスは、Boardクラスを受け取って、与えられたIDのcanvasタグに盤面状態を描画するクラスです。Boardのみに依存し、UpdateRuleLifeLikeCellularAutomatonからは独立しています。同じインターフェースで、divtableなどの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の整数配列を与えることで、個別のルールを表現できます。