Expirement - Conway's Game of Life in JavaScript

von Alexander Schwirjow

Die Ausgabe

Als erstes könnte man gleich die Ausgabe vorbereiten, da es ja sowieso die einzige Zeile in HTML ist, die gebraucht wird. Mit dem Canvas und Context ist es ja bekanntlich meistent das gleiche.

<canvas id="board" width="870" height="580"></canvas>

Bei JavaScript gibt es scheinbar auch nicht viel. Interessanterweise gibt dieses Beispiel durchaus genügend Spielraum zur Optimierung. Je nach dem wie sauber und effizient implementiert wurde, kann die Leistung schwanken - um die 60 FPS bei angenehmer Auflösung auf Mobilgeräten zu schaffen reicht es nicht, wenn der Code einfach funktioniert.

var canvas = document.getElementById('board');
var ctx = canvas.getContext("2d");

var game = {
	w: 90,
	h: 60,
	pcnt: 10,
	speed: 12,
	population: null,
	nextPopulation: null,
	loop: null,
	init: function() {
		this.population = [];
		for (var i = 0; i < this.w; i++) {
			var buffer = [];
			for (var j = 0; j < this.h; j++) {
				var alive = (this.random()) ? 1 : 0;
				buffer.push(alive);
			}
			this.population.push(buffer);
		}
		this.loop = window.setInterval(function() {
			game.draw();
			game.tick();
		}, 1000 / this.speed);
	},
	tick: function() {
		this.nextPopulation = new Array(this.w);
		for(var i = 0; i < this.w; i++) {
			this.nextPopulation[i] = new Array(this.h);
		}
		for(i = 0; i < this.w; i++) {
			for(var j = 0; j < this.h; j++) {
				this.check(i, j);
			}
		}
		this.population = this.nextPopulation;
	},
	check: function(x, y) {
		var alive = (this.population[x][y] === 1) ? true : false;
		nc = 0;
		for (var i = (x - 1); i <= (x + 1); i++) {
			for (var j = (y - 1); j <= (y + 1); j++) {
				if (!(i === x && j === y)) {
					if (this.isAlive(i, j)) {
						nc++;
					}
				}
			}
		}
		this.nextPopulation[x][y] = (alive) ? 1 : 0;
		if (alive && nc < 2) { this.nextPopulation[x][y] = 0; }
		if (alive && nc > 3) { this.nextPopulation[x][y] = 0; }
		if (!alive && nc === 3) { this.nextPopulation[x][y] = 1; }
		return alive;
	},
	isAlive: function(x, y) {
		if (typeof(this.population[x]) !== 'undefined' && typeof(this.population[x][y]) !== 'undefined') {
			return (this.population[x][y] == 1);
		}
		return false;
	},
	random: function() {
		return (Math.round(Math.random() * 100) > (100 - this.pcnt));
	},
	reset: function() {
		clearInterval(this.loop);
		game.init();
	},
	draw: function() {
		ctx.fillStyle = "#FFF";
		ctx.fillRect(0, 0, canvas.width, canvas.height);
		ctx.fillStyle = "#222";
		var sideH = canvas.width / this.w;
		var sideV = canvas.height / this.h;
		for (var i = 0; i < this.w; i++) {
			for (var j = 0; j < this.h; j++) {
				if (this.population[i][j] === 1) {
					var x = sideH * i;
					var y = sideV * j;
					ctx.fillRect(x, y, sideH, sideV);
				}
				
			}
		}
	}
}

game.init();

Zurück