コンウェイのライフゲーム
Conwayのライフゲームは、シンプルなルールから複雑さが生まれる典型的な例です。無限に広がるセルのグリッドを思い浮かべてください。各セルは「オン」(生存)または「オフ」(死亡)の状態を取ります。各ターン(「世代」)ごとに、すべてのセルが近傍セルの生存数をチェックします。セルが密集しすぎていると死亡し、孤立しすぎていても死亡します。しかし、ちょうど3つの近傍セルがあれば、死亡セルは生存状態に変化します。
このシンプルなルールセットは、無限に魅力的な挙動を生み出します。安定した構造や繰り返す振動子から、グリッドを横切る自己推進型の「宇宙船」まで様々です。これは、シンプルさから複雑さが生まれることの完璧な実証であり、チューリング完全でもあります。つまり、理論上はあらゆる計算をシミュレートできるのです。
ゲームボードを読み込み中...
パターン例
これらの古典的なパターンは、ゲームの創発的な挙動を示しています。「Glider」はグリッド上を移動し、「Pulsar」は魅惑的な振動を作り出し、「Small Exploder」はシンプルな形状が複雑な形態へと進化する様子を見せてくれます。
アルゴリズム
ライフゲームの核心には、このセルの宇宙における生死を決定する、いくつかのエレガントな関数があります:
// The core algorithm that determines life and death
function computeNextGeneration(currentGrid) {
const newGrid = createEmptyGrid(rows, cols);
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
const neighbors = countNeighbors(currentGrid, r, c);
const cellState = currentGrid[r][c];
// Apply Conway's rules of life
if (cellState === 1) {
// Live cell survives if it has 2 or 3 neighbors
newGrid[r][c] = (neighbors === 2 || neighbors === 3) ? 1 : 0;
} else {
// Dead cell springs to life if it has exactly 3 neighbors
newGrid[r][c] = (neighbors === 3) ? 1 : 0;
}
}
}
return newGrid;
}
コアアルゴリズムは、Conwayのルールをグリッド内の各セルに適用します。各世代ごとに、すべてのセルの状態と近傍セルの数をチェックし、そのセルが生存するか、死亡するか、または生存状態に変化するかを決定します。
// Count live neighbors for each cell
function countNeighbors(g, row, col) {
let count = 0;
for (let i = 0; i < 8; i++) {
const nr = row + NEIGHBOR_OFFSETS[i * 2];
const nc = col + NEIGHBOR_OFFSETS[i * 2 + 1];
if (nr >= 0 && nr < rows && nc >= 0 && nc < cols) {
count += g[nr][nc];
}
}
return count;
}