Fix #17, #14, #3, and update to v1.1.0-beta

This commit is contained in:
Riley Shaw 2014-09-19 13:53:51 -07:00
parent 7bf8132311
commit bb42f11306
7 changed files with 1062 additions and 37 deletions

View File

@ -117,9 +117,10 @@ var factory = (function () {
return { return {
x: step.x, x: step.x,
y: step.y, y: step.y,
creature: creature creature: creature,
observed: true
}; };
} else return false; } else return this.energy !== this.maxEnergy;
}; };
baseCA.prototype.boundEnergy = function () {}; baseCA.prototype.boundEnergy = function () {};

View File

@ -19,6 +19,7 @@ function Terrarium(width, height, id, cellSize, insertAfter) {
this.grid = []; this.grid = [];
this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter); this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter);
this.nextFrame = false; this.nextFrame = false;
this.hasChanged = false;
} }
/** /**
@ -67,8 +68,11 @@ Terrarium.prototype.step = function (steps) {
function copyAndRemoveInner (origCreature) { function copyAndRemoveInner (origCreature) {
if (origCreature) { if (origCreature) {
var copy = _.assign(new (origCreature.constructor)(), origCreature); var copy = _.assign(new (origCreature.constructor)(), origCreature);
var dead = copy && copy.isDead();
if (dead && !self.hasChanged) self.hasChanged = true;
copy.age++; copy.age++;
return copy && !copy.isDead() ? copy : false;
return !dead ? copy : false;
} else return false; } else return false;
} }
@ -76,6 +80,7 @@ Terrarium.prototype.step = function (steps) {
return _.map(origCols, copyAndRemoveInner); return _.map(origCols, copyAndRemoveInner);
} }
// TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner
function zipCoordsWithNeighbors (coords) { function zipCoordsWithNeighbors (coords) {
return { return {
coords: coords, coords: coords,
@ -101,16 +106,21 @@ Terrarium.prototype.step = function (steps) {
zipCoordsWithNeighbors zipCoordsWithNeighbors
); );
var result = creature.process(neighbors, x, y); var result = creature.process(neighbors, x, y);
if (result) { if (typeof result === 'object') {
var eigenColumn = eigenGrid[result.x]; var eigenColumn = eigenGrid[result.x];
if (!eigenColumn[result.y]) eigenColumn[result.y] = []; var returnedCreature = result.creature;
var returnedY = result.y;
eigenColumn[result.y].push({ if (!eigenColumn[returnedY]) eigenColumn[returnedY] = [];
eigenColumn[returnedY].push({
x: x, x: x,
y: y, y: y,
creature: result.creature creature: returnedCreature
}); });
if (!self.hasChanged && returnedCreature.observed) self.hasChanged = true;
} else { } else {
if (result && !self.hasChanged) self.hasChanged = true;
processLoser(creature); processLoser(creature);
} }
} }
@ -129,7 +139,7 @@ Terrarium.prototype.step = function (steps) {
if (!winnerCreature.successFn()) { if (!winnerCreature.successFn()) {
newGrid[winner.x][winner.y] = false; newGrid[winner.x][winner.y] = false;
} }
// TODO: so many calls to this. Can we just run it once at the start of a step?
winnerCreature.boundEnergy(); winnerCreature.boundEnergy();
// put the winner in its rightful place // put the winner in its rightful place
@ -146,6 +156,7 @@ Terrarium.prototype.step = function (steps) {
_.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); }); _.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); });
} }
var self = this;
var gridWidth = this.width; var gridWidth = this.width;
var gridHeight = this.height; var gridHeight = this.height;
var oldGrid = this.grid, newGrid, eigenGrid; var oldGrid = this.grid, newGrid, eigenGrid;
@ -153,6 +164,8 @@ Terrarium.prototype.step = function (steps) {
if (typeof steps !== 'number') steps = 1; if (typeof steps !== 'number') steps = 1;
while (steps--) { while (steps--) {
this.hasChanged = false;
oldGrid = newGrid ? _.clone(newGrid) : this.grid; oldGrid = newGrid ? _.clone(newGrid) : this.grid;
// copy the old grid & remove dead creatures // copy the old grid & remove dead creatures
@ -166,6 +179,8 @@ Terrarium.prototype.step = function (steps) {
// Choose a winner from each of the eigenGrid's superpositions // Choose a winner from each of the eigenGrid's superpositions
_.each(eigenGrid, pickWinner); _.each(eigenGrid, pickWinner);
if (!this.hasChanged) return false;
} }
return newGrid; return newGrid;
@ -179,19 +194,25 @@ Terrarium.prototype.draw = function () {
}; };
/** /**
* Starts animating the simulation * Starts animating the simulation. Can be called with only a function.
* @param {int} steps the simulation will stop after <steps> steps if specified * @param {int} steps the simulation will stop after <steps> steps if specified
* @param {Function} fn called as a callback once the animation finishes * @param {Function} fn called as a callback once the animation finishes
*/ */
Terrarium.prototype.animate = function (steps, fn) { Terrarium.prototype.animate = function (steps, fn) {
function tick () { function tick () {
self.grid = self.step(); var grid = self.step();
if (grid) {
self.grid = grid;
self.draw(); self.draw();
if (i++ !== steps) self.nextFrame = requestAnimationFrame(tick); if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick);
else { } // if grid hasn't changed || reached last step
self.nextFrame = false; self.nextFrame = false;
if (fn) fn(); if (fn) fn();
} }
if (typeof steps === 'function') {
fn = steps;
steps = null;
} }
if (!this.nextFrame) { if (!this.nextFrame) {

View File

@ -1,6 +1,6 @@
{ {
"name": "terra", "name": "terra",
"version": "1.0.0-beta", "version": "1.1.0-beta",
"homepage": "https://github.com/rileyjshaw/terra", "homepage": "https://github.com/rileyjshaw/terra",
"authors": [ "authors": [
"rileyjshaw <i@rileyjshaw.com>" "rileyjshaw <i@rileyjshaw.com>"

46
dist/terra.js vendored
View File

@ -128,9 +128,10 @@ var factory = (function () {
return { return {
x: step.x, x: step.x,
y: step.y, y: step.y,
creature: creature creature: creature,
observed: true
}; };
} else return false; } else return this.energy !== this.maxEnergy;
}; };
baseCA.prototype.boundEnergy = function () {}; baseCA.prototype.boundEnergy = function () {};
@ -313,6 +314,7 @@ function Terrarium(width, height, id, cellSize, insertAfter) {
this.grid = []; this.grid = [];
this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter); this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter);
this.nextFrame = false; this.nextFrame = false;
this.hasChanged = false;
} }
/** /**
@ -361,8 +363,11 @@ Terrarium.prototype.step = function (steps) {
function copyAndRemoveInner (origCreature) { function copyAndRemoveInner (origCreature) {
if (origCreature) { if (origCreature) {
var copy = _.assign(new (origCreature.constructor)(), origCreature); var copy = _.assign(new (origCreature.constructor)(), origCreature);
var dead = copy && copy.isDead();
if (dead && !self.hasChanged) self.hasChanged = true;
copy.age++; copy.age++;
return copy && !copy.isDead() ? copy : false;
return !dead ? copy : false;
} else return false; } else return false;
} }
@ -370,6 +375,7 @@ Terrarium.prototype.step = function (steps) {
return _.map(origCols, copyAndRemoveInner); return _.map(origCols, copyAndRemoveInner);
} }
// TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner
function zipCoordsWithNeighbors (coords) { function zipCoordsWithNeighbors (coords) {
return { return {
coords: coords, coords: coords,
@ -395,16 +401,21 @@ Terrarium.prototype.step = function (steps) {
zipCoordsWithNeighbors zipCoordsWithNeighbors
); );
var result = creature.process(neighbors, x, y); var result = creature.process(neighbors, x, y);
if (result) { if (typeof result === 'object') {
var eigenColumn = eigenGrid[result.x]; var eigenColumn = eigenGrid[result.x];
if (!eigenColumn[result.y]) eigenColumn[result.y] = []; var returnedCreature = result.creature;
var returnedY = result.y;
eigenColumn[result.y].push({ if (!eigenColumn[returnedY]) eigenColumn[returnedY] = [];
eigenColumn[returnedY].push({
x: x, x: x,
y: y, y: y,
creature: result.creature creature: returnedCreature
}); });
if (!self.hasChanged && returnedCreature.observed) self.hasChanged = true;
} else { } else {
if (result && !self.hasChanged) self.hasChanged = true;
processLoser(creature); processLoser(creature);
} }
} }
@ -423,7 +434,7 @@ Terrarium.prototype.step = function (steps) {
if (!winnerCreature.successFn()) { if (!winnerCreature.successFn()) {
newGrid[winner.x][winner.y] = false; newGrid[winner.x][winner.y] = false;
} }
// TODO: so many calls to this. Can we just run it once at the start of a step?
winnerCreature.boundEnergy(); winnerCreature.boundEnergy();
// put the winner in its rightful place // put the winner in its rightful place
@ -440,6 +451,7 @@ Terrarium.prototype.step = function (steps) {
_.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); }); _.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); });
} }
var self = this;
var gridWidth = this.width; var gridWidth = this.width;
var gridHeight = this.height; var gridHeight = this.height;
var oldGrid = this.grid, newGrid, eigenGrid; var oldGrid = this.grid, newGrid, eigenGrid;
@ -447,6 +459,8 @@ Terrarium.prototype.step = function (steps) {
if (typeof steps !== 'number') steps = 1; if (typeof steps !== 'number') steps = 1;
while (steps--) { while (steps--) {
this.hasChanged = false;
oldGrid = newGrid ? _.clone(newGrid) : this.grid; oldGrid = newGrid ? _.clone(newGrid) : this.grid;
// copy the old grid & remove dead creatures // copy the old grid & remove dead creatures
@ -460,6 +474,8 @@ Terrarium.prototype.step = function (steps) {
// Choose a winner from each of the eigenGrid's superpositions // Choose a winner from each of the eigenGrid's superpositions
_.each(eigenGrid, pickWinner); _.each(eigenGrid, pickWinner);
if (!this.hasChanged) return false;
} }
return newGrid; return newGrid;
@ -473,19 +489,25 @@ Terrarium.prototype.draw = function () {
}; };
/** /**
* Starts animating the simulation * Starts animating the simulation. Can be called with only a function.
* @param {int} steps the simulation will stop after <steps> steps if specified * @param {int} steps the simulation will stop after <steps> steps if specified
* @param {Function} fn called as a callback once the animation finishes * @param {Function} fn called as a callback once the animation finishes
*/ */
Terrarium.prototype.animate = function (steps, fn) { Terrarium.prototype.animate = function (steps, fn) {
function tick () { function tick () {
self.grid = self.step(); var grid = self.step();
if (grid) {
self.grid = grid;
self.draw(); self.draw();
if (i++ !== steps) self.nextFrame = requestAnimationFrame(tick); if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick);
else { } // if grid hasn't changed || reached last step
self.nextFrame = false; self.nextFrame = false;
if (fn) fn(); if (fn) fn();
} }
if (typeof steps === 'function') {
fn = steps;
steps = null;
} }
if (!this.nextFrame) { if (!this.nextFrame) {

983
dist/terra.min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -53,7 +53,7 @@ gulp.task('scripts', ['lint'], function() {
.pipe(source('terra.js')) .pipe(source('terra.js'))
.pipe(gulp.dest(paths.dist.scripts)) .pipe(gulp.dest(paths.dist.scripts))
.pipe($.rename('terra.min.js')) .pipe($.rename('terra.min.js'))
.pipe($.streamify( $.uglify() )) //.pipe($.streamify( $.uglify() ))
.pipe(gulp.dest(paths.dist.scripts)) .pipe(gulp.dest(paths.dist.scripts))
}); });
@ -76,7 +76,7 @@ gulp.task('sass', function () {
gulp.task('js_concat', ['demo'], function () { gulp.task('js_concat', ['demo'], function () {
return gulp.src(paths.demo.extraScripts.concat(paths.demo.temp + '/*.js')) return gulp.src(paths.demo.extraScripts.concat(paths.demo.temp + '/*.js'))
.pipe($.concat('terra.demo.min.js')) .pipe($.concat('terra.demo.min.js'))
.pipe($.streamify( $.uglify() )) //.pipe($.streamify( $.uglify() ))
.pipe(gulp.dest(paths.dist.demo)) .pipe(gulp.dest(paths.dist.demo))
}); });

View File

@ -1,6 +1,6 @@
{ {
"name": "terra", "name": "terra",
"version": "1.0.0-beta", "version": "1.1.0-beta",
"description": "A JavaScript library for simple biological simulations and cellular automata.", "description": "A JavaScript library for simple biological simulations and cellular automata.",
"main": "dist/terra.min.js", "main": "dist/terra.min.js",
"scripts": { "scripts": {