233 lines
6.7 KiB
JavaScript
Executable File
233 lines
6.7 KiB
JavaScript
Executable File
/*
|
|
* Copyright 2011 Julian Pulgarin
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
var Point = function(x, y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
};
|
|
|
|
var graphics = function() {
|
|
var canvas;
|
|
var ctx;
|
|
var canvasId;
|
|
|
|
var cellSize = 10; // pixels
|
|
var onColour = 'rgb(0, 200, 0)';
|
|
var offColour = 'rgb(200, 0, 0)';
|
|
var gridColour = 'rgb(50, 50, 50)';
|
|
|
|
var initCanvas = function(canvasId) {
|
|
this.canvas = $(canvasId).get(0);
|
|
this.ctx = this.canvas.getContext('2d');
|
|
this.canvasId = canvasId;
|
|
}
|
|
|
|
var drawCell = function(x, y, alive) {
|
|
var g = graphics;
|
|
g.ctx.fillStyle = (alive)? onColour : offColour;
|
|
g.ctx.fillRect(x * cellSize + 1, y * cellSize + 1, cellSize - 1, cellSize - 1);
|
|
}
|
|
|
|
var handleMouse = function(e) {
|
|
var l = life;
|
|
var g = graphics;
|
|
var that = this;
|
|
var cell = getCellPointUnderMouse(e);
|
|
var state;
|
|
processCell(cell);
|
|
$(g.canvasId).mousemove(function(e) {
|
|
cell = getCellPointUnderMouse(e);
|
|
processCell(cell);
|
|
});
|
|
function getCellPointUnderMouse(e) {
|
|
return new Point((e.pageX - that.offsetLeft) / g.cellSize | 0, ((e.pageY - that.offsetTop) / g.cellSize) | 0);
|
|
}
|
|
function processCell(cell) {
|
|
var x = cell.x;
|
|
var y = cell.y;
|
|
if (x > l.xCells - 1 || y > l.yCells - 1) {
|
|
return;
|
|
}
|
|
if (typeof state == 'undefined')
|
|
{
|
|
state = !l.prev[x][y];
|
|
}
|
|
l.prev[x][y] = state;
|
|
drawCell(x, y, state);
|
|
// TODO: Consider setting next as well
|
|
}
|
|
}
|
|
|
|
function paint() {
|
|
var g = graphics;
|
|
var l = life;
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
for (var y = 0; y < l.yCells; y++) {
|
|
g.drawCell(x, y, l.prev[x][y]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
canvas: canvas,
|
|
ctx: ctx,
|
|
canvasId: canvasId,
|
|
cellSize: cellSize,
|
|
onColour: onColour,
|
|
offColour: offColour,
|
|
gridColour: gridColour,
|
|
initCanvas: initCanvas,
|
|
drawCell: drawCell,
|
|
handleMouse: handleMouse,
|
|
paint: paint,
|
|
}
|
|
}();
|
|
|
|
var life = function() {
|
|
|
|
var yCells;
|
|
var xCells;
|
|
var prev = []; // previous generation
|
|
var next = []; // next generation
|
|
|
|
var _timeout;
|
|
var _alive = false;
|
|
|
|
var initUniverse = function(canvasId) {
|
|
var l = life;
|
|
var g = graphics;
|
|
g.initCanvas(canvasId);
|
|
l.xCells = ((g.canvas.width - 1) / g.cellSize) | 0;
|
|
l.yCells = ((g.canvas.height - 1) / g.cellSize) | 0;
|
|
g.ctx.fillStyle = g.offColour;
|
|
g.ctx.fillRect(0, 0, l.xCells * g.cellSize, l.yCells * g.cellSize);
|
|
g.ctx.fillStyle = g.gridColour;
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
l.prev[x] = [];
|
|
l.next[x] = [];
|
|
g.ctx.fillRect(x * g.cellSize, 0, 1, l.yCells * g.cellSize);
|
|
for(var y = 0; y < l.yCells; y++)
|
|
{
|
|
l.prev[x][y] = false;
|
|
}
|
|
}
|
|
g.ctx.fillRect(l.xCells * g.cellSize, 0, 1, l.yCells * g.cellSize);
|
|
for(var y = 0; y < l.yCells; y++)
|
|
{
|
|
g.ctx.fillRect(0, y * g.cellSize, l.xCells * g.cellSize, 1);
|
|
}
|
|
g.ctx.fillRect(0, l.yCells * g.cellSize, l.xCells * g.cellSize, 1);
|
|
$(canvasId).mousedown(g.handleMouse);
|
|
$('body').mouseup(function(e)
|
|
{
|
|
$(g.canvasId).unbind('mousemove');
|
|
});
|
|
}
|
|
|
|
var nextGen = function() {
|
|
var l = life;
|
|
var g = graphics;
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
for (var y = 0; y < l.yCells; y++) {
|
|
l.next[x][y] = l.prev[x][y];
|
|
}
|
|
}
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
for (var y = 0; y < l.yCells; y++) {
|
|
count = _neighbourCount(x, y);
|
|
|
|
// Game of Life rules
|
|
if (prev[x][y]) {
|
|
if (count < 2 || count > 3) {
|
|
next[x][y] = false;
|
|
}
|
|
} else if (count == 3) {
|
|
next[x][y] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
for (var y = 0; y < l.yCells; y++) {
|
|
l.prev[x][y] = l.next[x][y];
|
|
}
|
|
}
|
|
|
|
g.paint();
|
|
}
|
|
|
|
var toggleLife = function() {
|
|
var l = life;
|
|
|
|
if (!l._alive) {
|
|
l._alive = true;
|
|
l._timeout = setInterval("life.nextGen()", 100);
|
|
} else {
|
|
l._alive = false;
|
|
clearInterval(l._timeout);
|
|
}
|
|
}
|
|
|
|
var clear = function() {
|
|
var l = life;
|
|
var g = graphics;
|
|
|
|
for (var x = 0; x < l.xCells; x++) {
|
|
for (var y = 0; y < l.yCells; y++) {
|
|
l.prev[x][y] = false;
|
|
}
|
|
}
|
|
g.paint();
|
|
}
|
|
|
|
var _neighbourCount = function(x, y) {
|
|
var l = life;
|
|
var count = 0;
|
|
var neighbours = [
|
|
l.prev[x][(y - 1 + l.yCells) % l.yCells],
|
|
l.prev[(x + 1 + l.xCells) % l.xCells][(y - 1 + l.yCells) % l.yCells],
|
|
l.prev[(x + 1 + l.xCells) % l.xCells][y],
|
|
l.prev[(x + 1 + l.xCells) % l.xCells][(y + 1 + l.yCells) % l.yCells],
|
|
l.prev[x][(y + 1 + l.yCells) % l.yCells],
|
|
l.prev[(x - 1 + l.xCells) % l.xCells][(y + 1 + l.yCells) % l.yCells],
|
|
l.prev[(x - 1 + l.xCells) % l.xCells][y],
|
|
l.prev[(x - 1 + l.xCells) % l.xCells][(y - 1 + l.yCells) % l.yCells],
|
|
];
|
|
|
|
for (var i = 0; i < neighbours.length; i++) {
|
|
if (neighbours[i]) {
|
|
count++;
|
|
}
|
|
}
|
|
|
|
return count;
|
|
}
|
|
|
|
return {
|
|
yCells: yCells,
|
|
xCells: xCells,
|
|
prev: prev,
|
|
next: next,
|
|
initUniverse: initUniverse,
|
|
nextGen: nextGen,
|
|
toggleLife: toggleLife,
|
|
clear: clear,
|
|
}
|
|
}(); |