はじめに
(UI有りライフゲームは)初投稿です。
古来より伝わる「ライフゲーム」を色々な技術で実装するチャレンジやっております。
今回はHTMLでゲームに介入できるように実装してみます。
過去シリーズは以下です。
- ライフゲームをモダンな書き方で・Swift編・探究の章
- ライフゲームをモダンな書き方で・Swift編・発動の章
- ライフゲームをモダンな書き方で・Rust接触編
- ライフゲームをモダンな書き方で・Elixir接触編
- ライフゲームをElixir言語で並列化したかっただけなのに…
- Three.jsでゲーミングライフゲーム
- ライフゲームを立体空間に拡張(仮)
- 生Canvasでピクセルライフゲーム
ライフゲームとは
概要は先駆者様の解説に委ねます。
コーディングに必要なルールは以下となります。
- 2次元グリッド上で展開され、各セルは「生」または「死」の2つの状態を持つ
- 各世代で、「隣接(上下左右と斜め4方向の計8方向にある)」セルを以下のルールに従って状態が更新される
- 誕生:死んでいるセルに隣接する生きたセルがちょうど3つあれば、次の世代で誕生する
- 生存:生きているセルに隣接する生きたセルが2つまたは3つならば、次の世代でも生存する
- 過疎:生きているセルに隣接する生きたセルが1つ以下ならば、過疎により死滅する
- 過密::生きているセルに隣接する生きたセルが4つ以上ならば、過密により死滅する
- 端は常に死んでいるとみなす(固定境界)
開発環境
- MacStudio2023(M2 Max)
- macOS Tahoe(26.1)
動作確認
以下で公開
GitHub Page - InterventableLifeGame01
- 枠内でクリックすると、その位置のセルの生死が反転する
- NEXTボタンで世代が1進む
- PREVボタンで世代が1戻る
コード全体
以下で公開
GitHub - InterventableLifeGame01
世代ごとのパターンを保持
次の世代に移る前に現在のパターンを末尾に追加
// 現世代マップを保存
const nowMap = getCurrentGenerationMap(allCellMap);
generationMapArray.push(nowMap);
前の世代はパターン配列の末尾から取り出し、undefinedの場合は空なので中断
// 前世代マップを取得
const prevMap = generationMapArray.pop();
if (prevMap === undefined) {
return;
}
canvas要素内でのマウスクリックイベント
canvas内でのみイベントを有効にし、かつクリック座標をセルの座標系に変換する
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
// クリック座標をキャンバス内の論理座標に変換
const scaleX = canvas.width / rect.width;
const scaleY = canvas.height / rect.height;
const x = Math.floor((e.clientX - rect.left) * scaleX);
const y = Math.floor((e.clientY - rect.top) * scaleY);
const key = cellKey(x, y);
if (allCellMap.hasOwnProperty(key)) {
// 生死を反転
allCellMap[key].alive = !allCellMap[key].alive;
// 再描画
const color = getCellColor(allCellMap[key].alive, x + y);
drawPixel(x, y, color);
}
});
今後の展開予定
- 他のUIを追加して各ロジックへ介入可能にする
- ピクセル描画を用いた他アルゴリズム
- 他の技術でライフゲーム再現
参考文献
あとがき
単純ながらUIを追加してライフゲームの世代に対する編集が行えるようになったので、学術的な使い方ができたのが大きいです。
ここからライフゲームへの知見が進化が期待できそうです。
おわりに
ここまでご閲覧いただきありがとうございます。
今後も思いつくままに記事投稿を続けて行きたい所存であります。
全ては、「答え」のために。
