はじめに
(ライフゲームのルール拡張は)初投稿です。
古来より伝わる「ライフゲーム」を色々な技術で実装するチャレンジやっております。
今回は2次元空間での動作を前提としているライフゲームのルールを、3次元に拡張したルールでThree.jsを用いて表示させるトライやります。
過去シリーズは以下です。
- ライフゲームをモダンな書き方で・Swift編・探究の章
- ライフゲームをモダンな書き方で・Swift編・発動の章
- ライフゲームをモダンな書き方で・Rust接触編
- ライフゲームをモダンな書き方で・Elixir接触編
- ライフゲームをElixir言語で並列化したかっただけなのに…
- Three.jsでゲーミングライフゲーム
ライフゲームとは
概要は先駆者様の解説に委ねます。
一般的なルールは以下となります。
- 2次元グリッド上で展開され、各セルは「生」または「死」の2つの状態を持つ
- 各世代で、「隣接(上下左右と斜め4方向の計8方向にある)」セルを以下のルールに従って状態が更新される
- 誕生:死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代で誕生する
- 生存:生きているセルに隣接する生きたセルが2つまたは3つならば、次の世代でも生存する
- 過疎:生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する
- 過密::生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する
- 端は常に死んでいるとみなす(固定境界)
今回は「3次元グリッド上でのルール」を作成して動作させるのを目的とします。
開発環境
- MacStudio2023(M2 Max)
- macOS Tahoe(26.1)
- Node.js v20.11.0
- Three.js 0.160.0
実動作確認
以下に公開しています。
ルール拡張
まず3次元ルールがすでに学術的に決定しているかどうか検索しましたが、ネット上では見つけられませんでした。
発明・発見年表様のサイトでは独自ルールで実装されているようで、2次元ルールと近い確率の個数で求めている様子です。
ただ、ルールが厳しすぎるせいか大抵のパターンでは全滅してしまうという現実世界並みの厳しさがあるため、私は見た目が映えるのを優先して以下としました。
- 誕生:死んでいるセルに隣接する生きたセルがちょうど9(=3の二乗)個あれば、次の世代で誕生する
- 生存:生きているセルに隣接する生きたセルが4(=2の二乗)個以上9(=3の二乗)個ならば、次の世代でも生存する
- 過疎:生きているセルに隣接する生きたセルが4(=2の二乗)個未満ならば、過疎により死滅する
- 過密::生きているセルに隣接する生きたセルが9(=3の二乗)個を超えるならば、過密により死滅する
コード解説
全体は以下に公開しています。
表示部分は前回のロジックを踏襲しているので説明は割愛します。
シン・ライフゲームロジック
周りの生存数確認を3次元に拡張
// 生存数
const countAliveNeighbors = (cell, cellMap) => {
let result = 0;
for (let dy = -1; dy <= 1; dy++) {
for (let dz = -1; dz <= 1; dz++) {
for (let dx = -1; dx <= 1; dx++) {
if (dx == 0 && dz == 0 && dy == 0) continue; // 自分を除外
// 隣接するセルの座標を計算
const neighborX = cell.shape.userData.cellX + dx;
const neighborY = cell.shape.userData.cellY + dy;
const neighborZ = cell.shape.userData.cellZ + dz;
// 連想配列キー生成
const key = cellKey(neighborX, neighborY, neighborZ);
// 存在チェック
if (cellMap.hasOwnProperty(key)) {
const neighborCell = cellMap[key];
if (neighborCell.alive) {
result += 1;
}
}
}
}
}
return result;
};
生死判定の条件数値を変更
// 状態更新
const updateAliveFlag = (cell, alive, cellMap) => {
// 生存:生きているセルに隣接する生きたセル個数がmin以上max以下であれば、生存維持
const continueMin = 4; // 2^2
const continueMax = 9; // 3^2
// 過疎or過密:上記の条件を満たさない場合は、次の世代で死滅する
// 誕生:死んでいるセルに隣接する生きたセル個数がbirthCountであれば、次の世代で誕生する
const birthCount = 9; // 3^2
// 過疎or過密:上記の条件を満たさない場合は、次の世代で死滅する
const aliveCount = countAliveNeighbors(cell, cellMap);
if (alive && (aliveCount < continueMin || aliveCount > continueMax)) {
return false;
} else if (!alive && aliveCount == birthCount) {
return true;
} else {
return alive;
}
};
生死管理の拡張
セルの配置情報を含めて3次元に拡張
// 細胞群の初期化
const createCellMap = (w, h, d, layoutMap, scene) => {
const cells = {};
for (let y = 0; y < d; y++) {
for (let z = 0; z < h; z++) {
for (let x = 0; x < w; x++) {
let cell = {};
const shapeX = x - (w / 2);
const shapeY = y - (d / 2);
const shapeZ = z - (h / 2);
let alive = false;
if (layoutMap.hasOwnProperty(cellKey(x, y, z))) {
alive = layoutMap[cellKey(x, y, z)] > 0 ? true : false;
}
cell.alive = alive;
// 細胞の可視化
cell.shape = createShape(shapeX, shapeY, shapeZ, alive);
scene.add(cell.shape);
// 変数追加
cell.shape.userData.timer = 0.0;
cell.shape.userData.cellX = x;
cell.shape.userData.cellY = y;
cell.shape.userData.cellZ = z;
// 登録
const key = cellKey(x, y, z);
cells[key] = cell;
}
}
}
return cells;
}
今後の展開予定
- 特殊パターンの発見
- 他の技術でのチャレンジ
- ライフゲーム以外のロジックのチャレンジ
参考文献
あとがき
近年の技術革新を鑑みるにライフゲームの3次元化はすでに確立していると踏んだのですが、自分が調べた限りでは学術的情報が見つからす、とりあえずで自分のルールを制定して実装しました。
とはいえ同じ発想の人がそれなりの居ることも明らかになったので将来学術化する可能性は十分にあります。
それまでに自分でも色々と試してみようと思います。
おわりに
ここまでご閲覧いただきありがとうございます。
今後も思いつくままに記事投稿を続けて行きたい所存であります。
本当の闘いはこれからだ!


