Game of Life
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 BlockSet(blocks) { | |
this.blocks = blocks || [ | |
[] | |
]; | |
} | |
BlockSet.prototype = { | |
w: null, | |
h: null, | |
setBlock: function(x, y, val, arr) { | |
(arr ? arr : this.blocks)[y][x] = val; | |
}, | |
getBlock: function(x, y) { | |
return this.blocks[y][x]; | |
}, | |
emptyEqualSize: function() { | |
var blocks = []; | |
var i, colsLen, row; | |
for (i = 0, colsLen = this.blocks.length; i < colsLen; i++) { | |
blocks.push([]); | |
var j, rowLen; | |
for (j = 0,rowLen = this.blocks[i].length; j < rowLen; j++) { | |
blocks[i][j] = 0; | |
} | |
} | |
return blocks; | |
}, | |
iterateBlocks: function(fn) { | |
for (var y = 0; y < this.blocks.length; y++) { | |
for (var x = 0; x < this.blocks[0].length; x++) { | |
fn.call(this, x, y, this.getBlock(x, y)); | |
} | |
} | |
}, | |
iterateRows: function(fn) { | |
for (var y = 0, len = this.blocks.length; y < len; y++) { | |
fn.call(this, y); | |
} | |
}, | |
initBlocks: function() { | |
this.blocks = new Array(this.h); | |
for (var y = 0; y < this.h; y++) { | |
this.blocks[y] = new Array(this.w); | |
for (var x = 0; x < this.w; x++) { | |
this.blocks[y][x] = 0; | |
} | |
} | |
}, | |
zero: function() { | |
this.iterateBlocks(function(x, y) { | |
this.setBlock(x, y, 0); | |
}); | |
}, | |
grow: function() { | |
var newRow = new Array(this.w); | |
for (var i = 0, len = newRow.length; i < len; i++) { | |
newRow[i] = 0; | |
} | |
this.blocks.unshift(newRow.slice()); | |
this.blocks.push(newRow); | |
this.iterateRows(function(y) { | |
this.blocks[y].push(0); | |
this.blocks[y].unshift(0); | |
}); | |
this.w += 2; | |
this.h += 2; | |
}, | |
i: 0, | |
iterateSurrounding: function(x, y, fn) { | |
var i, j; | |
for (j = -1; j <= 1; j++) { | |
for (i = -1; i <= 1; i++) { | |
if ((j === 0 && i === 0) || x + i < 0 || x + i === this.w || y + j < 0 || y + j === this.h) { | |
continue; | |
} | |
this.i++; | |
fn.call(this, x + i, y + j); | |
} | |
} | |
} | |
}; |
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<title>Game of life</title> | |
<style type="text/css"> | |
* { | |
-webkit-user-select: none; | |
-moz-user-select: none; | |
outline: 0; | |
margin: 0; | |
padding: 0; | |
} | |
</style> | |
</head> | |
<body> | |
<canvas tabindex="0"></canvas> | |
<button value="start" unselectable="unselectable">start</button> | |
<button value="stop" unselectable="unselectable">stop</button> | |
<button value="clear" unselectable="unselectable">clear</button> | |
<button value="randomize" unselectable="unselectable">randomize</button> | |
<script type="text/javascript" src="pubsub.js"></script> | |
<script type="text/javascript" src="BlockSet.js"></script> | |
<script type="text/javascript" src="LifeGame.js"></script> | |
<script type="text/javascript" src="LifeView.js"></script> | |
<script type="text/javascript" src="main.js"></script> | |
</body> | |
</html> |
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
var LifeGame = function(w, h) { | |
this.w = w; | |
this.h = h; | |
var self = this; | |
var x, y; | |
PubSub.subscribe('draw', function(message, data) { | |
var value; | |
if (x === data[0] && y === data[1]) { | |
return; | |
} | |
x = data[0]; | |
y = data[1]; | |
self.setBlock(x, y, 1); | |
self.iterateSurrounding(x, y, function(sx, sy) { | |
self.setBlock(sx, sy, 1); | |
}); | |
PubSub.publish('frame', this); | |
}); | |
}; | |
LifeGame.prototype = new BlockSet; | |
var extension = { | |
frame: function() { | |
var nextStepBlocks = this.emptyEqualSize(); | |
this.iterateBlocks(function(x, y, val) { | |
var alive = 0, result = 0; | |
this.iterateSurrounding(x, y, function(nx, ny) { | |
if (this.getBlock(nx, ny) > 0) { | |
alive++; | |
} | |
}); | |
if (val && (alive === 2 || alive === 3) || alive === 3) { | |
result = 1; | |
} | |
this.setBlock(x, y, result, nextStepBlocks); | |
}); | |
this.blocks = nextStepBlocks; | |
PubSub.publish('frame', this); | |
this.frames++; | |
}, | |
stop: function() { | |
clearInterval(this.intervalId); | |
return { | |
frames: this.frames, | |
surroundingIterations: this.i | |
} | |
}, | |
start: function() { | |
this.intervalId = setInterval((function() { | |
this.frame(); | |
}).bind(this), this.intrvl); | |
}, | |
intervalId: null, | |
intrvl: 1000 / 10, | |
frames: 0, | |
preFill: function() { | |
this.iterateBlocks(function(x, y) { | |
this.setBlock(x, y, Math.random() * 100 > 50 ? 1 : 0); | |
}); | |
}, | |
setup: function() { | |
this.initBlocks(); | |
this.preFill(); | |
PubSub.publish('frame', this); | |
}, | |
reset: function() { | |
this.clear(); | |
this.setup(); | |
}, | |
clear: function() { | |
this.stop(); | |
this.frames = 0; | |
this.copy = null; | |
this.zero(); | |
PubSub.publish('frame', this); | |
} | |
}; | |
for (var k in extension) { | |
LifeGame.prototype[k] = extension[k]; | |
} |
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
var LifeView = function(canvas, game, tileSize) { | |
this.tileSize = tileSize; | |
this.canvas = canvas; | |
this.ctx = this.canvas.getContext("2d"); | |
this.game = game; | |
this.w = game.w; | |
this.h = game.h; | |
this.minInterval = 10; | |
var lastDraw = 0; | |
var passed = 0; | |
var drawing = false; | |
var draw = function(ev) { | |
if (!drawing) { | |
return; | |
} | |
PubSub.publish('draw', [Math.floor(ev.layerX / tileSize), Math.floor(ev.layerY / tileSize)]); | |
}; | |
canvas.addEventListener('mousemove', draw, false); | |
document.addEventListener('mousedown', function(ev) { | |
drawing = true; | |
draw(ev); | |
}, false); | |
document.addEventListener('mouseup', function(ev) { | |
drawing = false; | |
}, false); | |
PubSub.subscribe('frame', (function(msg, game) { | |
passed = Date.now() - lastDraw; | |
if (passed < this.minInterval) { | |
return; | |
} | |
this.draw(game); | |
lastDraw = Date.now(); | |
}).bind(this)); | |
this.colors = ['rgb(230, 230, 230)', 'black', 'cyan']; | |
}; | |
var viewExtension = { | |
draw: function() { | |
var self = this; | |
this.setSize(this.game.w * this.tileSize, this.game.h * this.tileSize); | |
this.w = this.game.w; | |
this.h = this.game.h; | |
game.iterateBlocks(function(x, y, val) { | |
self.ctx.fillStyle = self.colors[val]; | |
self.ctx.fillRect(self.tileSize * x, self.tileSize * y, self.tileSize, self.tileSize); | |
}); | |
}, | |
setSize: function(w, h) { | |
var c = this.canvas; | |
c.width = w; | |
c.height = h; | |
} | |
} | |
for (var k in viewExtension) { | |
LifeView.prototype[k] = viewExtension[k]; | |
} |
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
var canvas = document.getElementsByTagName('canvas')[0]; | |
var game = new LifeGame(400, 400); | |
var view = new LifeView(canvas, game, 2); | |
game.setup(); | |
var startBtn = document.querySelector('button[value=start]'); | |
var stopBtn = document.querySelector('button[value=stop]'); | |
var clearBtn = document.querySelector('button[value=clear]'); | |
var randomizeBtn = document.querySelector('button[value=randomize]'); | |
startBtn.addEventListener('click', function() { | |
game.start(); | |
}, false); | |
stopBtn.addEventListener('click', function() { | |
game.stop(); | |
}, false); | |
clearBtn.addEventListener('click', function() { | |
game.clear(); | |
}, false); | |
randomizeBtn.addEventListener('click', function() { | |
game.reset(); | |
}, false); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment