From 86440d10bfbae8434e44fbf4f79285c534477f6c Mon Sep 17 00:00:00 2001 From: Riley Shaw Date: Tue, 4 Nov 2014 04:48:30 -0500 Subject: [PATCH] Add periodic, neighborhood options; fix #22, fix #23, fix #24 --- app/creature.js | 7 ++- app/terrarium.js | 11 ++-- app/util.js | 107 +++++++++++++++++++++++++++++++++------ dist/terra.js | 125 +++++++++++++++++++++++++++++++++++++++------- dist/terra.min.js | 2 +- 5 files changed, 213 insertions(+), 39 deletions(-) diff --git a/app/creature.js b/app/creature.js index 14ef77b..7f13b81 100644 --- a/app/creature.js +++ b/app/creature.js @@ -123,6 +123,7 @@ var factory = (function () { } else return this.energy !== this.maxEnergy; }; + baseCA.prototype.actionRadius = 1; baseCA.prototype.boundEnergy = function () {}; baseCA.prototype.isDead = function () { return false; }; baseCA.prototype.process = function (neighbors, x, y) {}; @@ -154,7 +155,7 @@ var factory = (function () { }; } - var color = options.color; + var color = options.color || options.colour; // set the color randomly if none is provided if (typeof color !== 'object' || color.length !== 3) { options.color = [_.random(255), _.random(255), _.random(255)]; @@ -185,12 +186,14 @@ var factory = (function () { function () { init.call(this); } : function () {}; - var color = options.color; + var color = options.color = options.color || options.colour; // set the color randomly if none is provided if (typeof color !== 'object' || color.length !== 3) { options.color = [_.random(255), _.random(255), _.random(255)]; } + options.colorFn = options.colorFn || options.colourFn; + types[type].prototype = new baseCA(); types[type].prototype.constructor = types[type]; diff --git a/app/terrarium.js b/app/terrarium.js index 923d06f..2a75513 100644 --- a/app/terrarium.js +++ b/app/terrarium.js @@ -16,9 +16,13 @@ var dom = require('./dom.js'); * "background" option is required if trails is set * @param {array} background an RGB triplet for the canvas' background */ -function Terrarium(width, height, options) { +function Terrarium (width, height, options) { + var cellSize, neighborhood; options = options || {}; - var cellSize = options.cellSize || 10; + cellSize = options.cellSize || 10; + neighborhood = options.neighborhood || options.neighbourhood; + if (typeof neighborhood === 'string') neighborhood = neighborhood.toLowerCase(); + this.width = width; this.height = height; this.cellSize = cellSize; @@ -28,6 +32,7 @@ function Terrarium(width, height, options) { this.grid = []; this.nextFrame = false; this.hasChanged = false; + this.getNeighborCoords = _.getNeighborCoordsFn(width, height, neighborhood === 'vonneumann', options.periodic); } /** @@ -110,7 +115,7 @@ Terrarium.prototype.step = function (steps) { function processCreaturesInner (creature, x, y) { if (creature) { var neighbors = _.map( - _.getNeighborCoords(x, y, gridWidth - 1, gridHeight - 1, creature.actionRadius), + self.getNeighborCoords(x, y, creature.actionRadius), zipCoordsWithNeighbors ); var result = creature.process(neighbors, x, y); diff --git a/app/util.js b/app/util.js index 8ff3fe2..f6a713b 100644 --- a/app/util.js +++ b/app/util.js @@ -9,26 +9,105 @@ var _ = require('../lodash_custom/lodash.custom.min.js')._; * Takes a cell and returns the coordinates of its neighbors * @param {int} x0 - x position of cell * @param {int} y0 - y position of cell - * @param {int} xMax - maximum x index i.e. grid width - 1 - * @param {int} yMax - maximum x index i.e. grid height - 1 + * @param {int} xMax - maximum x index i.e. grid width + * @param {int} yMax - maximum x index i.e. grid height * @param {int} radius - (default = 1) neighbor radius * @return {array} - an array of [x, y] pairs of the neighboring cells */ -_.getNeighborCoords = function (x0, y0, xMax, yMax, radius) { - var coords = [], current, xLo, xHi, yLo, yHi; - if (typeof radius !== 'number' || radius < 1) radius = 1; +_.getNeighborCoordsFn = function (xMax, yMax, vonNeumann, periodic) { + if (periodic) { + if (vonNeumann) { + // periodic von neumann + return function (x0, y0, radius) { + var coords = [], x, rX, y, rY, rYMax; - xLo = Math.max(0, x0 - radius); - yLo = Math.max(0, y0 - radius); - xHi = Math.min(x0 + radius, xMax); - yHi = Math.min(y0 + radius, yMax); + for (rX = -radius; rX <= radius; ++rX) { + rYMax = radius - Math.abs(rX); + for (rY = -rYMax; rY <= rYMax; ++rY) { + x = ((rX + x0) % xMax + xMax) % xMax; + y = ((rY + y0) % yMax + yMax) % yMax; + if (x !== x0 || y !== y0) { + coords.push({ + x: x, + y: y + }); + } + } + } - for (var x = xLo; x <= xHi; x++) - for (var y = yLo; y <= yHi; y++) - if (x !== x0 || y !== y0) - coords.push({ x: x, y: y }); + return coords; + }; + } + else { + // periodic moore + return function (x0, y0, radius) { + var coords = [], x, xLo, xHi, y, yLo, yHi; - return coords; + xLo = x0 - radius; + yLo = y0 - radius; + xHi = x0 + radius; + yHi = y0 + radius; + + for (x = xLo; x <= xHi; ++x) { + for (y = yLo; y <= yHi; ++y) { + if (x !== x0 || y !== y0) { + coords.push({ + x: (x % xMax + xMax) % xMax, + y: (y % yMax + yMax) % yMax + }); + } + } + } + + return coords; + }; + } + } else { + // non-periodic, need to restrict to within [0, max) + xMax -= 1; + yMax -= 1; + + if (vonNeumann) { + //non-periodic von-neumann + return function (x0, y0, radius) { + var coords = [], x, rX, y, rY, rYMax; + + for (rX = -radius; rX <= radius; ++rX) { + rYMax = radius - Math.abs(rX); + for (rY = -rYMax; rY <= rYMax; ++rY) { + x = rX + x0; + y = rY + y0; + if (x >= 0 && y >=0 && x <= xMax && y <= yMax && (x !== x0 || y !== y0)) { + coords.push({ + x: x, + y: y + }); + } + } + } + + return coords; + }; + } + else { + // non-periodic moore + return function (x0, y0, radius) { + var coords = [], x, xLo, xHi, y, yLo, yHi; + + xLo = Math.max(0, x0 - radius); + yLo = Math.max(0, y0 - radius); + xHi = Math.min(x0 + radius, xMax); + yHi = Math.min(y0 + radius, yMax); + + for (x = xLo; x <= xHi; ++x) + for (y = yLo; y <= yHi; ++y) + if (x !== x0 || y !== y0) + coords.push({ x: x, y: y }); + + return coords; + }; + } + } }; _.pickRandomWeighted = function (weightedArrays) { diff --git a/dist/terra.js b/dist/terra.js index d5d5f68..763cc70 100755 --- a/dist/terra.js +++ b/dist/terra.js @@ -134,6 +134,7 @@ var factory = (function () { } else return this.energy !== this.maxEnergy; }; + baseCA.prototype.actionRadius = 1; baseCA.prototype.boundEnergy = function () {}; baseCA.prototype.isDead = function () { return false; }; baseCA.prototype.process = function (neighbors, x, y) {}; @@ -165,7 +166,7 @@ var factory = (function () { }; } - var color = options.color; + var color = options.color || options.colour; // set the color randomly if none is provided if (typeof color !== 'object' || color.length !== 3) { options.color = [_.random(255), _.random(255), _.random(255)]; @@ -196,12 +197,14 @@ var factory = (function () { function () { init.call(this); } : function () {}; - var color = options.color; + var color = options.color = options.color || options.colour; // set the color randomly if none is provided if (typeof color !== 'object' || color.length !== 3) { options.color = [_.random(255), _.random(255), _.random(255)]; } + options.colorFn = options.colorFn || options.colourFn; + types[type].prototype = new baseCA(); types[type].prototype.constructor = types[type]; @@ -317,9 +320,13 @@ var dom = require('./dom.js'); * "background" option is required if trails is set * @param {array} background an RGB triplet for the canvas' background */ -function Terrarium(width, height, options) { +function Terrarium (width, height, options) { + var cellSize, neighborhood; options = options || {}; - var cellSize = options.cellSize || 10; + cellSize = options.cellSize || 10; + neighborhood = options.neighborhood || options.neighbourhood; + if (typeof neighborhood === 'string') neighborhood = neighborhood.toLowerCase(); + this.width = width; this.height = height; this.cellSize = cellSize; @@ -329,6 +336,7 @@ function Terrarium(width, height, options) { this.grid = []; this.nextFrame = false; this.hasChanged = false; + this.getNeighborCoords = _.getNeighborCoordsFn(width, height, neighborhood === 'vonneumann', options.periodic); } /** @@ -411,7 +419,7 @@ Terrarium.prototype.step = function (steps) { function processCreaturesInner (creature, x, y) { if (creature) { var neighbors = _.map( - _.getNeighborCoords(x, y, gridWidth - 1, gridHeight - 1, creature.actionRadius), + self.getNeighborCoords(x, y, creature.actionRadius), zipCoordsWithNeighbors ); var result = creature.process(neighbors, x, y); @@ -553,26 +561,105 @@ var _ = require('../lodash_custom/lodash.custom.min.js')._; * Takes a cell and returns the coordinates of its neighbors * @param {int} x0 - x position of cell * @param {int} y0 - y position of cell - * @param {int} xMax - maximum x index i.e. grid width - 1 - * @param {int} yMax - maximum x index i.e. grid height - 1 + * @param {int} xMax - maximum x index i.e. grid width + * @param {int} yMax - maximum x index i.e. grid height * @param {int} radius - (default = 1) neighbor radius * @return {array} - an array of [x, y] pairs of the neighboring cells */ -_.getNeighborCoords = function (x0, y0, xMax, yMax, radius) { - var coords = [], current, xLo, xHi, yLo, yHi; - if (typeof radius !== 'number' || radius < 1) radius = 1; +_.getNeighborCoordsFn = function (xMax, yMax, vonNeumann, periodic) { + if (periodic) { + if (vonNeumann) { + // periodic von neumann + return function (x0, y0, radius) { + var coords = [], x, rX, y, rY, rYMax; - xLo = Math.max(0, x0 - radius); - yLo = Math.max(0, y0 - radius); - xHi = Math.min(x0 + radius, xMax); - yHi = Math.min(y0 + radius, yMax); + for (rX = -radius; rX <= radius; ++rX) { + rYMax = radius - Math.abs(rX); + for (rY = -rYMax; rY <= rYMax; ++rY) { + x = ((rX + x0) % xMax + xMax) % xMax; + y = ((rY + y0) % yMax + yMax) % yMax; + if (x !== x0 || y !== y0) { + coords.push({ + x: x, + y: y + }); + } + } + } - for (var x = xLo; x <= xHi; x++) - for (var y = yLo; y <= yHi; y++) - if (x !== x0 || y !== y0) - coords.push({ x: x, y: y }); + return coords; + }; + } + else { + // periodic moore + return function (x0, y0, radius) { + var coords = [], x, xLo, xHi, y, yLo, yHi; - return coords; + xLo = x0 - radius; + yLo = y0 - radius; + xHi = x0 + radius; + yHi = y0 + radius; + + for (x = xLo; x <= xHi; ++x) { + for (y = yLo; y <= yHi; ++y) { + if (x !== x0 || y !== y0) { + coords.push({ + x: (x % xMax + xMax) % xMax, + y: (y % yMax + yMax) % yMax + }); + } + } + } + + return coords; + }; + } + } else { + // non-periodic, need to restrict to within [0, max) + xMax -= 1; + yMax -= 1; + + if (vonNeumann) { + //non-periodic von-neumann + return function (x0, y0, radius) { + var coords = [], x, rX, y, rY, rYMax; + + for (rX = -radius; rX <= radius; ++rX) { + rYMax = radius - Math.abs(rX); + for (rY = -rYMax; rY <= rYMax; ++rY) { + x = rX + x0; + y = rY + y0; + if (x >= 0 && y >=0 && x <= xMax && y <= yMax && (x !== x0 || y !== y0)) { + coords.push({ + x: x, + y: y + }); + } + } + } + + return coords; + }; + } + else { + // non-periodic moore + return function (x0, y0, radius) { + var coords = [], x, xLo, xHi, y, yLo, yHi; + + xLo = Math.max(0, x0 - radius); + yLo = Math.max(0, y0 - radius); + xHi = Math.min(x0 + radius, xMax); + yHi = Math.min(y0 + radius, yMax); + + for (x = xLo; x <= xHi; ++x) + for (y = yLo; y <= yHi; ++y) + if (x !== x0 || y !== y0) + coords.push({ x: x, y: y }); + + return coords; + }; + } + } }; _.pickRandomWeighted = function (weightedArrays) { diff --git a/dist/terra.min.js b/dist/terra.min.js index 8d8e706..2346f2f 100755 --- a/dist/terra.min.js +++ b/dist/terra.min.js @@ -1 +1 @@ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.terra=t()}}(function(){var t;return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!u&&c)return c(a,!0);if(i)return i(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var s=n[a]={exports:{}};t[a][0].call(s.exports,function(e){var n=t[a][1][e];return o(n?n:e)},s,s.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;athis.maxEnergy&&(this.energy=this.maxEnergy)},t.prototype.isDead=function(){return this.energy<=0},t.prototype.reproduce=function(t){var e=n.filter(t,function(t){return!t.creature});if(e.length){var o=e[n.random(e.length-1)],i=o.coords,a=r.make(this.type),u=function(){return this.energy-=this.initialEnergy,!0}.bind(this),c=this.wait;return{x:i.x,y:i.y,creature:a,successFn:u,failureFn:c}}return!1},t.prototype.move=function(t){var e=this,r=n.filter(t,function(t){return t.creature.sizen*this.reproduceLv&&this.reproduce?e=this.reproduce(t):this.energy>n*this.moveLv&&this.move&&(e=this.move(t));var r=e.creature;return r?(r.successFn=e.successFn||r.wait,r.failureFn=e.failureFn||r.wait,{x:e.x,y:e.y,creature:r,observed:!0}):this.energy!==this.maxEnergy},e.prototype.boundEnergy=function(){},e.prototype.isDead=function(){return!1},e.prototype.process=function(){},e.prototype.wait=function(){};var o={};return{make:function(t,e){var n=o[t];return n?new n(e):!1},registerCreature:function(e,r){var i=e.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){this.energy=this.initialEnergy,r.call(this)}:function(){this.energy=this.initialEnergy};var a=e.color;return("object"!=typeof a||3!==a.length)&&(e.color=[n.random(255),n.random(255),n.random(255)]),o[i].prototype=new t,o[i].prototype.constructor=o[i],n.each(e,function(t,e){o[i].prototype[e]=t}),o[i].prototype.successFn=o[i].wait,o[i].prototype.failureFn=o[i].wait,o[i].prototype.energy=e.initialEnergy,!0}return!1},registerCA:function(t,r){var i=t.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){r.call(this)}:function(){};var a=t.color;return("object"!=typeof a||3!==a.length)&&(t.color=[n.random(255),n.random(255),n.random(255)]),o[i].prototype=new e,o[i].prototype.constructor=o[i],n.each(t,function(t,e){o[i].prototype[e]=t}),!0}return!1}}}();e.exports=r},{"./util.js":6}],3:[function(t,e){var n=t("./util.js");e.exports=function(t,e,r,o,i){var a=t.getContext("2d");if(o&&i)a.fillStyle="rgba("+i+","+(1-o)+")",a.fillRect(0,0,t.width,t.height);else{if(o)throw"Background must also be set for trails";a.clearRect(0,0,t.width,t.height)}n.each(e,function(t,e){n.each(t,function(t,n){if(t){var o=t.colorFn?t.colorFn():t.color+","+t.energy/t.maxEnergy;a.fillStyle="rgba("+o+")",t.character?a.fillText(t.character,e*r,n*r+r):a.fillRect(e*r,n*r,r,r)}})})}},{"./util.js":6}],4:[function(t,e){var n=function(t,e,n,r,o,i){function a(o){var a=document.createElement("canvas"),u=a.getContext("2d");return o=function(){var t=document.createElement("canvas").getContext("2d"),e=window.devicePixelRatio||1,n=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/n}(),a.width=t*o,a.height=e*o,a.style.width=t+"px",a.style.height=e+"px",u.setTransform(o,0,0,o,0,0),u.font="bold "+n+"px Arial",r&&(a.id=r),i&&(a.style.background="rgb("+i+")"),a}t*=n,e*=n;var u=a();return o?o.parentNode.insertBefore(u,o.nextSibling):document.body.appendChild(u),u};e.exports={createCanvasElement:n}},{}],5:[function(t,e){function n(t,e,n){n=n||{};var r=n.cellSize||10;this.width=t,this.height=e,this.cellSize=r,this.trails=n.trails,this.background=n.background,this.canvas=a.createCanvasElement(t,e,r,n.id,n.insertAfter,this.background),this.grid=[],this.nextFrame=!1,this.hasChanged=!1}var r=t("./util"),o=t("./creature.js"),i=t("./display.js"),a=t("./dom.js");n.prototype.makeGrid=function(t){for(var e=[],n=typeof t,r=0,i=this.width;i>r;r++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[r].push(o.make("function"===n?t(r,a):"object"===n&&t.length?(t[a]||[])[r]:"string"===n?t:void 0))}return e},n.prototype.makeGridWithDistribution=function(t){for(var e=[],n=0,i=this.width;i>n;n++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[n].push(o.make(r.pickRandomWeighted(t)))}return e},n.prototype.step=function(t){function e(t){if(t){var e=r.assign(new t.constructor,t),n=e&&e.isDead();return n&&!p.hasChanged&&(p.hasChanged=!0),e.age++,n?!1:e}return!1}function n(t){return r.map(t,e)}function o(t){return{coords:t,creature:g[t.x][t.y]}}function i(t){var e=t.creature;e?(e.failureFn(),e.boundEnergy()):(t.wait(),t.boundEnergy())}function a(t,e,n){if(t){var a=r.map(r.getNeighborCoords(e,n,h-1,y-1,t.actionRadius),o),u=t.process(a,e,n);if("object"==typeof u){var c=l[u.x],f=u.creature,s=u.y;c[s]||(c[s]=[]),c[s].push({x:e,y:n,creature:f}),!p.hasChanged&&f.observed&&(p.hasChanged=!0)}else u&&!p.hasChanged&&(p.hasChanged=!0),i(t)}}function u(t,e){r.each(t,function(t,n){a(t,e,n)})}function c(t,e,n){if(t){var o=t.splice(r.random(t.length-1),1)[0],a=o.creature;a.successFn()||(s[o.x][o.y]=!1),a.boundEnergy(),s[e][n]=a,r.each(t,i)}}function f(t,e){r.each(t,function(t,n){c(t,e,n)})}var s,l,p=this,h=this.width,y=this.height,g=this.grid;for("number"!=typeof t&&(t=1);t--;)if(this.hasChanged=!1,g=s?r.clone(s):this.grid,s=r.map(g,n),l=this.makeGrid(),r.each(s,u),r.each(l,f),!this.hasChanged)return!1;return s},n.prototype.draw=function(){i(this.canvas,this.grid,this.cellSize,this.trails,this.background)},n.prototype.animate=function(t,e){function n(){var i=o.step();return i&&(o.grid=i,o.draw(),++r!==t)?o.nextFrame=requestAnimationFrame(n):(o.nextFrame=!1,void(e&&e()))}if("function"==typeof t&&(e=t,t=null),!this.nextFrame){var r=0,o=this;o.nextFrame=requestAnimationFrame(n)}},n.prototype.stop=function(){cancelAnimationFrame(this.nextFrame),this.nextFrame=!1},e.exports=n},{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(t,e){t("../bower_components/seedrandom/seedrandom.js")("terra :)",{global:!0});var n=t("../lodash_custom/lodash.custom.min.js")._;n.getNeighborCoords=function(t,e,n,r,o){var i,a,u,c,f=[];("number"!=typeof o||1>o)&&(o=1),i=Math.max(0,t-o),u=Math.max(0,e-o),a=Math.min(t+o,n),c=Math.min(e+o,r);for(var s=i;a>=s;s++)for(var l=u;c>=l;l++)(s!==t||l!==e)&&f.push({x:s,y:l});return f},n.pickRandomWeighted=function(t){var e,r,o=0,i=n.random(100,!0);for(r=0,_len=t.length;_len>r;r++)if(e=t[r],o+=e[1],o>i)return e[0];return!1},e.exports=n},{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":8}],7:[function(e,n){!function(t,e,n,r,o,i,a,u,c){function f(t){var e,n=t.length,o=this,i=0,a=o.i=o.j=0,u=o.S=[];for(n||(t=[n++]);r>i;)u[i]=i++;for(i=0;r>i;i++)u[i]=u[a=v&a+t[i%n]+(e=u[i])],u[a]=e;(o.g=function(t){for(var e,n=0,i=o.i,a=o.j,u=o.S;t--;)e=u[i=v&i+1],n=n*r+u[v&(u[i]=u[a=v&a+e])+(u[a]=e)];return o.i=i,o.j=a,n})(r)}function s(t,e){var n,r=[],o=typeof t;if(e&&"object"==o)for(n in t)try{r.push(s(t[n],e-1))}catch(i){}return r.length?r:"string"==o?t:t+"\x00"}function l(t,e){for(var n,r=t+"",o=0;ot;)t=(t+n)*r,e*=r,n=m.g(1);for(;t>=d;)t/=2,e/=2,n>>>=1;return(t+n)/e},v,"global"in i?i.global:this==n)};l(n[c](),e),a&&a.exports?a.exports=m:u&&u.amd&&u(function(){return m})}(this,[],Math,256,6,52,"object"==typeof n&&n,"function"==typeof t&&t,"random")},{}],8:[function(t,e,n){(function(t){(function(){function r(t){return"function"!=typeof t.toString&&"string"==typeof(t+"")}function o(t){t.length=0,A.lengthn?0:n);++rk;k++)r+="n='"+n.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",n.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+n.g+"}";r+="}"}return(n.b||xe.nonEnumArgs)&&(r+="}"),r+=n.c+";return E",t("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",e+r+"}")(s,T,ee,ce,D,d,we,b,V.f,ne,H,be,U,re,oe)}function g(t){return"function"==typeof t&&ie.test(t)}function d(t){return t&&"object"==typeof t&&"number"==typeof t.length&&oe.call(t)==L||!1}function v(t){return"function"==typeof t}function m(t){return!(!t||!H[typeof t])}function b(t){return"string"==typeof t||t&&"object"==typeof t&&oe.call(t)==U||!1}function x(t,e,n){var r=[];if(e=a.createCallback(e,n,3),we(t)){n=-1;for(var o=t.length;++narguments.length;if(e=a.createCallback(e,r,4),we(t)){var i=-1,u=t.length;for(o&&(n=t[++i]);++i3&&typeof a[c-2]=='function'){var e=d(a[--c-1],a[c--],2)}else if(c>2&&typeof a[c-1]=='function'){e=a[--c]}"),g:"E[n]=e?e(E[n],t[n]):t[n]"}),Se=y(Z,_e,{j:!1}),Oe=y(Z,_e);v(/x/)&&(v=function(t){return"function"==typeof t&&"[object Function]"==oe.call(t)}),a.assign=Fe,a.bind=_,a.createCallback=function(t,e,n){var r=typeof t;if(null==t||"function"==r)return s(t,e,n);if("object"!=r)return O(t);var o=Ce(t),i=o[0],a=t[i];return 1!=o.length||a!==a||m(a)?function(e){for(var n=o.length,r=!1;n--&&(r=p(e[o[n]],t[o[n]],null,!0)););return r}:function(t){return t=t[i],a===t&&(0!==a||1/a==1/t)}},a.filter=x,a.forEach=j,a.forIn=Se,a.forOwn=Oe,a.keys=Ce,a.map=w,a.property=O,a.collect=w,a.each=j,a.extend=Fe,a.select=x,a.clone=function(t,e,n,r){return"boolean"!=typeof e&&null!=e&&(r=n,n=e,e=!1),c(t,e,"function"==typeof n&&s(n,r,1))},a.identity=F,a.isArguments=d,a.isArray=we,a.isFunction=v,a.isObject=m,a.isString=b,a.noop=S,a.random=function(t,e,n){var r=null==t,o=null==e;return null==n&&("boolean"==typeof t&&o?(n=t,t=1):o||"boolean"!=typeof e||(n=e,o=!0)),r&&o&&(e=1),t=+t||0,o?(e=t,t=0):e=+e||0,n||t%1||e%1?(n=ve(),de(t+n*(e-t+parseFloat("1e-"+((n+"").length-1))),e)):t+ae(ve()*(e-t+1))},a.reduce=E,a.some=C,a.any=C,a.foldl=E,a.inject=E,a.VERSION="2.4.1",X&&Y&&(X._=a)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1])(1)}); \ No newline at end of file +!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e.terra=t()}}(function(){var t;return function e(t,n,r){function o(a,u){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!u&&c)return c(a,!0);if(i)return i(a,!0);var f=new Error("Cannot find module '"+a+"'");throw f.code="MODULE_NOT_FOUND",f}var s=n[a]={exports:{}};t[a][0].call(s.exports,function(e){var n=t[a][1][e];return o(n?n:e)},s,s.exports,e,t,n,r)}return n[a].exports}for(var i="function"==typeof require&&require,a=0;athis.maxEnergy&&(this.energy=this.maxEnergy)},t.prototype.isDead=function(){return this.energy<=0},t.prototype.reproduce=function(t){var e=n.filter(t,function(t){return!t.creature});if(e.length){var o=e[n.random(e.length-1)],i=o.coords,a=r.make(this.type),u=function(){return this.energy-=this.initialEnergy,!0}.bind(this),c=this.wait;return{x:i.x,y:i.y,creature:a,successFn:u,failureFn:c}}return!1},t.prototype.move=function(t){var e=this,r=n.filter(t,function(t){return t.creature.sizen*this.reproduceLv&&this.reproduce?e=this.reproduce(t):this.energy>n*this.moveLv&&this.move&&(e=this.move(t));var r=e.creature;return r?(r.successFn=e.successFn||r.wait,r.failureFn=e.failureFn||r.wait,{x:e.x,y:e.y,creature:r,observed:!0}):this.energy!==this.maxEnergy},e.prototype.actionRadius=1,e.prototype.boundEnergy=function(){},e.prototype.isDead=function(){return!1},e.prototype.process=function(){},e.prototype.wait=function(){};var o={};return{make:function(t,e){var n=o[t];return n?new n(e):!1},registerCreature:function(e,r){var i=e.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){this.energy=this.initialEnergy,r.call(this)}:function(){this.energy=this.initialEnergy};var a=e.color||e.colour;return("object"!=typeof a||3!==a.length)&&(e.color=[n.random(255),n.random(255),n.random(255)]),o[i].prototype=new t,o[i].prototype.constructor=o[i],n.each(e,function(t,e){o[i].prototype[e]=t}),o[i].prototype.successFn=o[i].wait,o[i].prototype.failureFn=o[i].wait,o[i].prototype.energy=e.initialEnergy,!0}return!1},registerCA:function(t,r){var i=t.type;if("string"==typeof i&&"undefined"==typeof o[i]){o[i]="function"==typeof r?function(){r.call(this)}:function(){};var a=t.color=t.color||t.colour;return("object"!=typeof a||3!==a.length)&&(t.color=[n.random(255),n.random(255),n.random(255)]),t.colorFn=t.colorFn||t.colourFn,o[i].prototype=new e,o[i].prototype.constructor=o[i],n.each(t,function(t,e){o[i].prototype[e]=t}),!0}return!1}}}();e.exports=r},{"./util.js":6}],3:[function(t,e){var n=t("./util.js");e.exports=function(t,e,r,o,i){var a=t.getContext("2d");if(o&&i)a.fillStyle="rgba("+i+","+(1-o)+")",a.fillRect(0,0,t.width,t.height);else{if(o)throw"Background must also be set for trails";a.clearRect(0,0,t.width,t.height)}n.each(e,function(t,e){n.each(t,function(t,n){if(t){var o=t.colorFn?t.colorFn():t.color+","+t.energy/t.maxEnergy;a.fillStyle="rgba("+o+")",t.character?a.fillText(t.character,e*r,n*r+r):a.fillRect(e*r,n*r,r,r)}})})}},{"./util.js":6}],4:[function(t,e){var n=function(t,e,n,r,o,i){function a(o){var a=document.createElement("canvas"),u=a.getContext("2d");return o=function(){var t=document.createElement("canvas").getContext("2d"),e=window.devicePixelRatio||1,n=t.webkitBackingStorePixelRatio||t.mozBackingStorePixelRatio||t.msBackingStorePixelRatio||t.oBackingStorePixelRatio||t.backingStorePixelRatio||1;return e/n}(),a.width=t*o,a.height=e*o,a.style.width=t+"px",a.style.height=e+"px",u.setTransform(o,0,0,o,0,0),u.font="bold "+n+"px Arial",r&&(a.id=r),i&&(a.style.background="rgb("+i+")"),a}t*=n,e*=n;var u=a();return o?o.parentNode.insertBefore(u,o.nextSibling):document.body.appendChild(u),u};e.exports={createCanvasElement:n}},{}],5:[function(t,e){function n(t,e,n){var o,i;n=n||{},o=n.cellSize||10,i=n.neighborhood||n.neighbourhood,"string"==typeof i&&(i=i.toLowerCase()),this.width=t,this.height=e,this.cellSize=o,this.trails=n.trails,this.background=n.background,this.canvas=a.createCanvasElement(t,e,o,n.id,n.insertAfter,this.background),this.grid=[],this.nextFrame=!1,this.hasChanged=!1,this.getNeighborCoords=r.getNeighborCoordsFn(t,e,"vonneumann"===i,n.periodic)}var r=t("./util"),o=t("./creature.js"),i=t("./display.js"),a=t("./dom.js");n.prototype.makeGrid=function(t){for(var e=[],n=typeof t,r=0,i=this.width;i>r;r++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[r].push(o.make("function"===n?t(r,a):"object"===n&&t.length?(t[a]||[])[r]:"string"===n?t:void 0))}return e},n.prototype.makeGridWithDistribution=function(t){for(var e=[],n=0,i=this.width;i>n;n++){e.push([]);for(var a=0,u=this.height;u>a;a++)e[n].push(o.make(r.pickRandomWeighted(t)))}return e},n.prototype.step=function(t){function e(t){if(t){var e=r.assign(new t.constructor,t),n=e&&e.isDead();return n&&!p.hasChanged&&(p.hasChanged=!0),e.age++,n?!1:e}return!1}function n(t){return r.map(t,e)}function o(t){return{coords:t,creature:h[t.x][t.y]}}function i(t){var e=t.creature;e?(e.failureFn(),e.boundEnergy()):(t.wait(),t.boundEnergy())}function a(t,e,n){if(t){var a=r.map(p.getNeighborCoords(e,n,t.actionRadius),o),u=t.process(a,e,n);if("object"==typeof u){var c=l[u.x],f=u.creature,s=u.y;c[s]||(c[s]=[]),c[s].push({x:e,y:n,creature:f}),!p.hasChanged&&f.observed&&(p.hasChanged=!0)}else u&&!p.hasChanged&&(p.hasChanged=!0),i(t)}}function u(t,e){r.each(t,function(t,n){a(t,e,n)})}function c(t,e,n){if(t){var o=t.splice(r.random(t.length-1),1)[0],a=o.creature;a.successFn()||(s[o.x][o.y]=!1),a.boundEnergy(),s[e][n]=a,r.each(t,i)}}function f(t,e){r.each(t,function(t,n){c(t,e,n)})}var s,l,p=this,h=(this.width,this.height,this.grid);for("number"!=typeof t&&(t=1);t--;)if(this.hasChanged=!1,h=s?r.clone(s):this.grid,s=r.map(h,n),l=this.makeGrid(),r.each(s,u),r.each(l,f),!this.hasChanged)return!1;return s},n.prototype.draw=function(){i(this.canvas,this.grid,this.cellSize,this.trails,this.background)},n.prototype.animate=function(t,e){function n(){var i=o.step();return i&&(o.grid=i,o.draw(),++r!==t)?o.nextFrame=requestAnimationFrame(n):(o.nextFrame=!1,void(e&&e()))}if("function"==typeof t&&(e=t,t=null),!this.nextFrame){var r=0,o=this;o.nextFrame=requestAnimationFrame(n)}},n.prototype.stop=function(){cancelAnimationFrame(this.nextFrame),this.nextFrame=!1},e.exports=n},{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(t,e){t("../bower_components/seedrandom/seedrandom.js")("terra :)",{global:!0});var n=t("../lodash_custom/lodash.custom.min.js")._;n.getNeighborCoordsFn=function(t,e,n,r){return r?n?function(n,r,o){var i,a,u,c,f,s=[];for(a=-o;o>=a;++a)for(f=o-Math.abs(a),c=-f;f>=c;++c)i=((a+n)%t+t)%t,u=((c+r)%e+e)%e,(i!==n||u!==r)&&s.push({x:i,y:u});return s}:function(n,r,o){var i,a,u,c,f,s,l=[];for(a=n-o,f=r-o,u=n+o,s=r+o,i=a;u>=i;++i)for(c=f;s>=c;++c)(i!==n||c!==r)&&l.push({x:(i%t+t)%t,y:(c%e+e)%e});return l}:(t-=1,e-=1,n?function(n,r,o){var i,a,u,c,f,s=[];for(a=-o;o>=a;++a)for(f=o-Math.abs(a),c=-f;f>=c;++c)i=a+n,u=c+r,i>=0&&u>=0&&t>=i&&e>=u&&(i!==n||u!==r)&&s.push({x:i,y:u});return s}:function(n,r,o){var i,a,u,c,f,s,l=[];for(a=Math.max(0,n-o),f=Math.max(0,r-o),u=Math.min(n+o,t),s=Math.min(r+o,e),i=a;u>=i;++i)for(c=f;s>=c;++c)(i!==n||c!==r)&&l.push({x:i,y:c});return l})},n.pickRandomWeighted=function(t){var e,r,o=0,i=n.random(100,!0);for(r=0,_len=t.length;_len>r;r++)if(e=t[r],o+=e[1],o>i)return e[0];return!1},e.exports=n},{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":8}],7:[function(e,n){!function(t,e,n,r,o,i,a,u,c){function f(t){var e,n=t.length,o=this,i=0,a=o.i=o.j=0,u=o.S=[];for(n||(t=[n++]);r>i;)u[i]=i++;for(i=0;r>i;i++)u[i]=u[a=v&a+t[i%n]+(e=u[i])],u[a]=e;(o.g=function(t){for(var e,n=0,i=o.i,a=o.j,u=o.S;t--;)e=u[i=v&i+1],n=n*r+u[v&(u[i]=u[a=v&a+e])+(u[a]=e)];return o.i=i,o.j=a,n})(r)}function s(t,e){var n,r=[],o=typeof t;if(e&&"object"==o)for(n in t)try{r.push(s(t[n],e-1))}catch(i){}return r.length?r:"string"==o?t:t+"\x00"}function l(t,e){for(var n,r=t+"",o=0;ot;)t=(t+n)*r,e*=r,n=m.g(1);for(;t>=d;)t/=2,e/=2,n>>>=1;return(t+n)/e},v,"global"in i?i.global:this==n)};l(n[c](),e),a&&a.exports?a.exports=m:u&&u.amd&&u(function(){return m})}(this,[],Math,256,6,52,"object"==typeof n&&n,"function"==typeof t&&t,"random")},{}],8:[function(t,e,n){(function(t){(function(){function r(t){return"function"!=typeof t.toString&&"string"==typeof(t+"")}function o(t){t.length=0,A.lengthn?0:n);++rk;k++)r+="n='"+n.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",n.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+n.g+"}";r+="}"}return(n.b||xe.nonEnumArgs)&&(r+="}"),r+=n.c+";return E",t("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",e+r+"}")(s,T,ee,ce,R,d,je,b,V.f,ne,H,be,U,re,oe)}function y(t){return"function"==typeof t&&ie.test(t)}function d(t){return t&&"object"==typeof t&&"number"==typeof t.length&&oe.call(t)==z||!1}function v(t){return"function"==typeof t}function m(t){return!(!t||!H[typeof t])}function b(t){return"string"==typeof t||t&&"object"==typeof t&&oe.call(t)==U||!1}function x(t,e,n){var r=[];if(e=a.createCallback(e,n,3),je(t)){n=-1;for(var o=t.length;++narguments.length;if(e=a.createCallback(e,r,4),je(t)){var i=-1,u=t.length;for(o&&(n=t[++i]);++i3&&typeof a[c-2]=='function'){var e=d(a[--c-1],a[c--],2)}else if(c>2&&typeof a[c-1]=='function'){e=a[--c]}"),g:"E[n]=e?e(E[n],t[n]):t[n]"}),Se=g(Z,Fe,{j:!1}),Oe=g(Z,Fe);v(/x/)&&(v=function(t){return"function"==typeof t&&"[object Function]"==oe.call(t)}),a.assign=ke,a.bind=F,a.createCallback=function(t,e,n){var r=typeof t;if(null==t||"function"==r)return s(t,e,n);if("object"!=r)return O(t);var o=Ce(t),i=o[0],a=t[i];return 1!=o.length||a!==a||m(a)?function(e){for(var n=o.length,r=!1;n--&&(r=p(e[o[n]],t[o[n]],null,!0)););return r}:function(t){return t=t[i],a===t&&(0!==a||1/a==1/t)}},a.filter=x,a.forEach=w,a.forIn=Se,a.forOwn=Oe,a.keys=Ce,a.map=j,a.property=O,a.collect=j,a.each=w,a.extend=ke,a.select=x,a.clone=function(t,e,n,r){return"boolean"!=typeof e&&null!=e&&(r=n,n=e,e=!1),c(t,e,"function"==typeof n&&s(n,r,1))},a.identity=_,a.isArguments=d,a.isArray=je,a.isFunction=v,a.isObject=m,a.isString=b,a.noop=S,a.random=function(t,e,n){var r=null==t,o=null==e;return null==n&&("boolean"==typeof t&&o?(n=t,t=1):o||"boolean"!=typeof e||(n=e,o=!0)),r&&o&&(e=1),t=+t||0,o?(e=t,t=0):e=+e||0,n||t%1||e%1?(n=ve(),de(t+n*(e-t+parseFloat("1e-"+((n+"").length-1))),e)):t+ae(ve()*(e-t+1))},a.reduce=E,a.some=C,a.any=C,a.foldl=E,a.inject=E,a.VERSION="2.4.1",X&&Y&&(X._=a)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1])(1)}); \ No newline at end of file