Skip to content

Instantly share code, notes, and snippets.

@fedelebron
Created June 28, 2011 11:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fedelebron/1050972 to your computer and use it in GitHub Desktop.
Save fedelebron/1050972 to your computer and use it in GitHub Desktop.
Game of Life in JS
(function() {
function GameOfLife(config) {
this.width = Number(config.width || 400);
this.height = Number(config.height || 300);
this.rows = Number(config.rows || 80);
this.cols = Number(config.cols || 120);
this.cellHeight = this.height / this.rows;
this.cellWidth = (this.width-1) / this.cols;
this.generations = Number(config.generations || 1000);
this.gridData = config.grid || [];
if(!config.canvas || !config.canvas.getContext) {
throw 'I need a canvas to work with.';
}
this.canvas = config.canvas;
this.context = this.canvas.getContext('2d');
if(!this.context) {
throw 'Could not create context.';
}
this.generation = 0;
this.liveCellCache = [];
this.grid = [];
this.createGrid();
};
GameOfLife.prototype.createGrid = function() {
var elementIndex,
element,
context = this.context,
x,
y,
rows = this.rows,
cols = this.cols,
cellHeight = this.cellHeight,
cellWidth = this.cellWidth
;
for(var elementIndex in this.gridData) {
element = this.gridData[elementIndex];
this.grid[element[0] * this.cols + element[1]] = true;
this.liveCellCache.push(element);
}
context.fillStyle = 'grey';
for(x = 0; x < rows; x++) {
context.fillRect(0, x*cellHeight, this.width, 1);
for(y = 0; y <= cols; y++) {
context.fillRect(y * cellWidth, 1, 1, x * cellHeight);
}
}
};
GameOfLife.prototype.advanceGeneration = function() {
var x,
y,
newGrid = [],
rows = this.rows,
cols = this.cols,
currentCache = this.liveCellCache,
cacheLength = currentCache.length,
newCache = [],
dx,
dy,
tx,
ty,
x,
y,
i;
this.clearState();
for(i = 0; i < cacheLength; i++) {
x = currentCache[i][0];
y = currentCache[i][1];
for(dx = -1; dx <= 1; dx++) {
for(dy = -1; dy <= 1; dy++) {
tx = x + dx;
ty = y + dy;
if(this.shouldPaint(tx, ty)) {
newGrid[tx * cols + ty] = true;
newCache = this.addToCache(newCache, [tx, ty]);
}
}
}
}
this.grid = newGrid;
this.liveCellCache = newCache;
this.paint();
};
GameOfLife.prototype.addToCache = function(cache, tuple) {
var i,
len = cache.length;
for(i = 0; i < len; i++) {
if(cache[i][0] == tuple[0] && cache[i][1] == tuple[1]) {
return cache;
}
}
cache.push(tuple);
return cache;
};
GameOfLife.prototype.clearState = function() {
var context = this.context,
currentCache = this.liveCellCache,
cacheLength = currentCache.length,
cellWidth = this.cellWidth,
cellHeight = this.cellHeight,
i
;
this.context.fillStyle = 'white';
for(i = 0; i < cacheLength; i++) {
context.fillRect(currentCache[i][1] * cellWidth + 1, currentCache[i][0] * cellHeight + 1, cellWidth - 1, cellHeight - 1) ;
}
};
GameOfLife.prototype.paint = function() {
var context = this.context,
x,
y,
grid = this.grid,
rows = this.rows,
cols = this.cols,
cellWidth = this.cellWidth,
cellHeight = this.cellHeight,
i,
currentCache = this.liveCellCache,
cacheLength = currentCache.length;
context.fillStyle = 'black';
for(i = 0; i < cacheLength; i++) {
context.fillRect(currentCache[i][1] * cellWidth + 1, currentCache[i][0] * cellHeight + 1, cellWidth - 1, cellHeight - 1);
}
};
GameOfLife.prototype.shouldPaint = function(x, y) {
var neighbourCount = this.countLiveNeighbours(x, y);
if(this.isAlive(x, y)) {
if(neighbourCount < 2 || neighbourCount > 3) {
return false;
}
return true;
} else {
if(neighbourCount == 3) {
return true;
}
return false;
}
};
GameOfLife.prototype.isAlive = function(x, y) {
if(x < 0 || x > this.rows) { return false; }
if(y < 0 || y > this.cols) { return false; }
return this.grid[x * this.cols + y] == true;
};
GameOfLife.prototype.countLiveNeighbours = function(x, y) {
var count = 0;
this.isAlive(x-1, y) && count++;
this.isAlive(x-1, y+1) && count++;
this.isAlive(x-1, y-1) && count++;
this.isAlive(x+1, y) && count++;
this.isAlive(x+1, y+1) && count++;
this.isAlive(x+1, y-1) && count++;
this.isAlive(x, y+1) && count++;
this.isAlive(x, y-1) && count++;
return count;
};
GameOfLife.prototype.generate = function() {
var that = this;
if(this.generation++ < this.generations) {
this.advanceGeneration();
setTimeout(function() { that.generate() }, 1);
}
};
function setUpGame () {
//var glider = [[5, 7], [5, 6], [5, 8], [4, 8], [3, 7]];
//var acorn = new Acorn();
//var diehard = new Diehard();
//var acorn2 = new Acorn();
var longChain = new Long();
var canvas = document.getElementById('canvas'),
game,
rows = 160,
cols = 200;
//diehard.setOffset(Math.floor(rows / 2), Math.floor(cols/2));
//acorn.setOffset(Math.floor(rows * 0.5), Math.floor(cols * 0.75));
//acorn2.setOffset(Math.floor(rows * 0.5), Math.floor(cols * 0.5));
longChain.setOffset(Math.floor(rows / 2), Math.floor(cols*0.1));
game = new GameOfLife({ width: canvas.width,
height: canvas.height,
grid: longChain.getPositions(),//acorn.getPositions().concat(acorn2.getPositions()),
canvas: canvas,
rows: rows,
cols: cols
});
game.generate();
}
function Shape(positions) {
this.offsetX = 0;
this.offsetY = 0;
this.positions = positions || [];
};
Shape.prototype.setOffset = function(x, y) {
this.offsetX = Number(x);
this.offsetY = Number(y);
};
Shape.prototype.getPositions = function() {
var that = this;
return this.positions.map(function(e) { return [e[0]+that.offsetX, e[1]+that.offsetY]; });
};
function Acorn() {
this.positions = [[4, 2], [4, 3], [2, 3], [3, 5], [4, 6], [4, 7], [4, 8]];
};
Acorn.prototype = Shape.prototype;
function Diehard() {
this.positions = [[3, 2], [3, 3], [4, 3], [4, 7], [4, 8], [4, 9], [2, 8]];
};
Diehard.prototype = Shape.prototype;
function Long() {
this.positions = [[2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 11], [2, 12], [2, 13], [2, 14], [2, 15], [2, 19], [2, 20], [2, 21], [2, 28], [2, 29], [2, 30], [2, 31], [2, 32], [2, 33], [2, 34], [2, 36], [2, 37], [2, 38], [2, 39], [2, 40]]
};
Long.prototype = Shape.prototype;
$(setUpGame);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment