[JS]HTML5 Canvasを使ったライフゲーム

HTML5 で導入された要素 Canvas には自由に図形を描画でき、JavaScript と組み合わせることでリアルタイムに書き換えることができます。
そこで練習も兼ねて HTML5 + JavaScript でシンプルなライフゲームを作ってみました。

lifegame

Demo

・html 部分

<!DOCTYPE html>
<html>
<head>
<title>Game of Life</title>
<meta charset="utf-8">
<script src="lifegame.js"></script>
</head>
<body>

<div style="margin: 1em 0;">
<form>
<input type="button" value="Random" id="buttonRandom">
<input type="button" value="Start" id="buttonStart">
<input type="button" value="Reset" id="buttonReset">
</form>
</div>

<canvas id="lifegame" width="320" height="320"></canvas>
</body>
</html>

・スクリプト部分(lifegame.js)

var canvas;
var ctx;
var cellSize = 8;	// セル1マスのサイズ
var cols;
var rows;
var cells = new Array();
var buttonStart;
var buttonRandom;
var buttonReset;
var timer1;
var running = false;

window.onload = function()
{
	canvas = document.getElementById('lifegame');
	ctx = canvas.getContext('2d');
	cols = Math.floor(canvas.width / cellSize);
	rows = Math.floor(canvas.height / cellSize);
	initCells();
	buttonStart = document.getElementById('buttonStart');
	buttonRandom = document.getElementById('buttonRandom');
	buttonReset = document.getElementById('buttonReset');
	buttonStart.addEventListener('click', onStart, false);
	buttonRandom.addEventListener('click', randomCells, false);
	buttonReset.addEventListener('click', initCells, false);
	canvas.addEventListener('click', canvasClick, false);
};

// 開始
function onStart(){
	if(running){
		clearInterval(timer1);
		buttonStart.value = "Start";
		running = false;
	} else {
		nextGeneration();
		timer1 = setInterval("nextGeneration()", 100);
		buttonStart.value = "Stop";
		running = true;
	}
}

// 初期化
function initCells(){
	ctx.fillStyle = 'rgb(60, 60, 60)';
	ctx.fillRect(0,0, canvas.width, canvas.height);
	for(col=0;col<cols;col++){
		cells[col] = new Array();
		for(row=0;row<rows;row++){
			cells[col][row] = 0;
		}
	}
	redraw();
}

// ランダムに埋める
function randomCells(){
	for(col=0;col<cols;col++){
		cells[col] = new Array();
		for(row=0;row<rows;row++){
			cells[col][row] = Math.round( Math.random());
		}
	}
	redraw();
}

// 全体を再描画
function redraw(){
	for(col=0;col<cols;col++){
		for(row=0;row<rows;row++){
			drawCell(col, row);
		}
	}
}

// セルを描画
function drawCell(x, y){
	var value = cells[x][y];
	var style = value ? "rgb(156, 255,0)" : "rgb(40,40,40)"; 
    ctx.fillStyle = style;
	ctx.fillRect(x * cellSize, y * cellSize,
		cellSize - 1, cellSize - 1);
}

// 世代を進行させる
function nextGeneration(){
	var tmpCells = new Array();
	for(col=0;col<cols;col++){
		tmpCells[col] = new Array();
		for(row=0;row<rows;row++){
			var count = countAround(col, row);
			if(cells[col][row]){
				if(count == 2 || count == 3){
					tmpCells[col][row] = 1;
				} else {
					tmpCells[col][row] = 0;
				}
			} else {
				if(count == 3){
					tmpCells[col][row] = 1;
				} else {
					tmpCells[col][row] = 0;
				}
			}
		}
	}
	cells = tmpCells;
	redraw();
}

// 周囲の生存セルを数える
function countAround(x, y){
	var count = 0;
	for(i=-1;i<=1;i++){
		for(j=-1;j<=1;j++){
			if(
				(i != 0 || j != 0) &&
				x + i >= 0 && x + i < cols &&
				y + j >= 0 && y + j < rows
			) {
				count += cells[x + i][y + j];
			}
		}
	}
	return count;
}

// Canvasクリック
function canvasClick(e){
    var x = e.clientX - canvas.offsetLeft;
    var y = e.clientY - canvas.offsetTop;
	var col = Math.floor(x / cellSize);
	var row = Math.floor(y / cellSize);
	cells[col][row] = !cells[col][row];
	drawCell(col, row);
}

ランダムボタンでランダムにセルを埋めるかマウスクリックでセルの ON/OFF を設定してスタートボタンで開始します。
セルの周囲に2~3の生存セルが隣接している場合生き残り、過疎(1以下)や過密(4以上)の場合消滅します。
死んでいるセルの周囲にちょうど3つの生存セルが隣接している場合生きているセルが発生します。