Chapter 88

ライフゲーム

miku
miku
2021.11.19に更新

ライフゲームはイギリスの数学者コンウェイが考案したシミュレーションゲームで、単純なルールで生命の誕生、進化、淘汰の過程を表現することができる。

実行例

アルゴリズム

各セルには生と死の 2 つの状態があり、次の更新で周りのセルの状態によって自身の状態が決まる。

状態 現在 条件
誕生 生きているセルが周りに 3 つある
生存 生きているセルが周りに 2 つか 3 つある
過疎 生きているセルが周りに 1 つ以下
過密 生きているセルが周りに 4 つ以上
// 自身は死んでいて、周りに生きているセルが3つあったら次に生き返る
if (board[i] === CellType.dead) {
  if (aliveCellNum === 3) {
    drawBoard[i] = CellType.alive;
  }
}
// 自身は生きていて、周りに生きているセルが2つか3つあったらそのまま生存
// でなければ死ぬ
else {
  if (aliveCellNum === 2 || aliveCellNum === 3) {
    drawBoard[i] = CellType.alive;
  } else {
    drawBoard[i] = CellType.dead;
  }
}

コードを書くときの注意点

変化の条件にある通り、周りの状態を参照する必要があるため、計算済みの状態を参照しないように別配列を用意して結果をそちらに書き込む必要がある。

コード例

const Color = {
  stroke: "#000",
  fill: "#009ad6",
};
const CellType = {
  alive: "alive",
  dead: "dead",
};
const cellWidth = 60;
const cellHeight = 40;
const cellSize = 10;
let board = [];
let drawBoard = [];

function setup() {
  createCanvas(windowWidth, windowHeight);

  board = [];
  drawBoard = [];
  for (let i = 0; i < cellWidth * cellHeight; i++) {
    board[i] = drawBoard[i] = random(100) < 50 ? CellType.alive : CellType.dead;
  }

  stroke(Color.stroke);
}

function draw() {
  for (let i = 0; i < cellWidth * cellHeight; i++) {
    const x = i % cellWidth;
    const y = floor(i / cellWidth);
    let aliveCellNum = 0;
    for (let yy = -1; yy <= 1; yy++) {
      for (let xx = -1; xx <= 1; xx++) {
        if (xx === 0 && yy === 0) {
          continue;
        }
        const tx = x + xx;
        const ty = y + yy;
        if (tx < 0 || cellWidth <= tx || ty < 0 || cellHeight <= ty) {
          continue;
        }

        const ti = ty * cellWidth + tx;
        if (board[ti] === CellType.alive) {
          aliveCellNum++;
        }
      }
    }

    if (board[i] === CellType.dead) {
      if (aliveCellNum === 3) {
        drawBoard[i] = CellType.alive;
      }
    } else {
      if (aliveCellNum === 2 || aliveCellNum === 3) {
        drawBoard[i] = CellType.alive;
      } else {
        drawBoard[i] = CellType.dead;
      }
    }
  }

  clear();
  for (let y = 0; y < cellHeight; y++) {
    for (let x = 0; x < cellWidth; x++) {
      const index = y * cellWidth + x;

      noFill();
      if (drawBoard[index] === CellType.alive) {
        fill(Color.fill);
      }
      const tx = x * cellSize;
      const ty = y * cellSize;
      rect(tx, ty, cellSize, cellSize);
    }
  }

  board = drawBoard.slice();
}