Skip to content

Instantly share code, notes, and snippets.

@kosmotaur
Created October 18, 2011 16:23
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save kosmotaur/1295863 to your computer and use it in GitHub Desktop.
Game of Life
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);
}
}
}
};
<!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>
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];
}
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];
}
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