Created
June 28, 2011 11:41
-
-
Save fedelebron/1050972 to your computer and use it in GitHub Desktop.
Game of Life in JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(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