diff --git a/app/creature.js b/app/creature.js index 90d2336..14ef77b 100644 --- a/app/creature.js +++ b/app/creature.js @@ -117,9 +117,10 @@ var factory = (function () { return { x: step.x, y: step.y, - creature: creature + creature: creature, + observed: true }; - } else return false; + } else return this.energy !== this.maxEnergy; }; baseCA.prototype.boundEnergy = function () {}; diff --git a/app/terrarium.js b/app/terrarium.js index 2ca1762..8dcf8fe 100644 --- a/app/terrarium.js +++ b/app/terrarium.js @@ -19,6 +19,7 @@ function Terrarium(width, height, id, cellSize, insertAfter) { this.grid = []; this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter); this.nextFrame = false; + this.hasChanged = false; } /** @@ -67,8 +68,11 @@ Terrarium.prototype.step = function (steps) { function copyAndRemoveInner (origCreature) { if (origCreature) { var copy = _.assign(new (origCreature.constructor)(), origCreature); + var dead = copy && copy.isDead(); + if (dead && !self.hasChanged) self.hasChanged = true; copy.age++; - return copy && !copy.isDead() ? copy : false; + + return !dead ? copy : false; } else return false; } @@ -76,6 +80,7 @@ Terrarium.prototype.step = function (steps) { return _.map(origCols, copyAndRemoveInner); } + // TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner function zipCoordsWithNeighbors (coords) { return { coords: coords, @@ -101,16 +106,21 @@ Terrarium.prototype.step = function (steps) { zipCoordsWithNeighbors ); var result = creature.process(neighbors, x, y); - if (result) { + if (typeof result === 'object') { 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, y: y, - creature: result.creature + creature: returnedCreature }); + if (!self.hasChanged && returnedCreature.observed) self.hasChanged = true; } else { + if (result && !self.hasChanged) self.hasChanged = true; processLoser(creature); } } @@ -129,7 +139,7 @@ Terrarium.prototype.step = function (steps) { if (!winnerCreature.successFn()) { 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(); // 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); }); } + var self = this; var gridWidth = this.width; var gridHeight = this.height; var oldGrid = this.grid, newGrid, eigenGrid; @@ -153,6 +164,8 @@ Terrarium.prototype.step = function (steps) { if (typeof steps !== 'number') steps = 1; while (steps--) { + this.hasChanged = false; + oldGrid = newGrid ? _.clone(newGrid) : this.grid; // 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 _.each(eigenGrid, pickWinner); + + if (!this.hasChanged) return false; } 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 if specified * @param {Function} fn called as a callback once the animation finishes */ Terrarium.prototype.animate = function (steps, fn) { function tick () { - self.grid = self.step(); - self.draw(); - if (i++ !== steps) self.nextFrame = requestAnimationFrame(tick); - else { - self.nextFrame = false; - if (fn) fn(); - } + var grid = self.step(); + if (grid) { + self.grid = grid; + self.draw(); + if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick); + } // if grid hasn't changed || reached last step + self.nextFrame = false; + if (fn) fn(); + } + + if (typeof steps === 'function') { + fn = steps; + steps = null; } if (!this.nextFrame) { diff --git a/bower.json b/bower.json index a23755b..272effc 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "terra", - "version": "1.0.0-beta", + "version": "1.1.0-beta", "homepage": "https://github.com/rileyjshaw/terra", "authors": [ "rileyjshaw " diff --git a/dist/terra.js b/dist/terra.js index 18312fe..6838012 100755 --- a/dist/terra.js +++ b/dist/terra.js @@ -128,9 +128,10 @@ var factory = (function () { return { x: step.x, y: step.y, - creature: creature + creature: creature, + observed: true }; - } else return false; + } else return this.energy !== this.maxEnergy; }; baseCA.prototype.boundEnergy = function () {}; @@ -313,6 +314,7 @@ function Terrarium(width, height, id, cellSize, insertAfter) { this.grid = []; this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter); this.nextFrame = false; + this.hasChanged = false; } /** @@ -361,8 +363,11 @@ Terrarium.prototype.step = function (steps) { function copyAndRemoveInner (origCreature) { if (origCreature) { var copy = _.assign(new (origCreature.constructor)(), origCreature); + var dead = copy && copy.isDead(); + if (dead && !self.hasChanged) self.hasChanged = true; copy.age++; - return copy && !copy.isDead() ? copy : false; + + return !dead ? copy : false; } else return false; } @@ -370,6 +375,7 @@ Terrarium.prototype.step = function (steps) { return _.map(origCols, copyAndRemoveInner); } + // TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner function zipCoordsWithNeighbors (coords) { return { coords: coords, @@ -395,16 +401,21 @@ Terrarium.prototype.step = function (steps) { zipCoordsWithNeighbors ); var result = creature.process(neighbors, x, y); - if (result) { + if (typeof result === 'object') { 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, y: y, - creature: result.creature + creature: returnedCreature }); + if (!self.hasChanged && returnedCreature.observed) self.hasChanged = true; } else { + if (result && !self.hasChanged) self.hasChanged = true; processLoser(creature); } } @@ -423,7 +434,7 @@ Terrarium.prototype.step = function (steps) { if (!winnerCreature.successFn()) { 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(); // 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); }); } + var self = this; var gridWidth = this.width; var gridHeight = this.height; var oldGrid = this.grid, newGrid, eigenGrid; @@ -447,6 +459,8 @@ Terrarium.prototype.step = function (steps) { if (typeof steps !== 'number') steps = 1; while (steps--) { + this.hasChanged = false; + oldGrid = newGrid ? _.clone(newGrid) : this.grid; // 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 _.each(eigenGrid, pickWinner); + + if (!this.hasChanged) return false; } 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 if specified * @param {Function} fn called as a callback once the animation finishes */ Terrarium.prototype.animate = function (steps, fn) { function tick () { - self.grid = self.step(); - self.draw(); - if (i++ !== steps) self.nextFrame = requestAnimationFrame(tick); - else { - self.nextFrame = false; - if (fn) fn(); - } + var grid = self.step(); + if (grid) { + self.grid = grid; + self.draw(); + if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick); + } // if grid hasn't changed || reached last step + self.nextFrame = false; + if (fn) fn(); + } + + if (typeof steps === 'function') { + fn = steps; + steps = null; } if (!this.nextFrame) { diff --git a/dist/terra.min.js b/dist/terra.min.js index b7cb20f..6838012 100755 --- a/dist/terra.min.js +++ b/dist/terra.min.js @@ -1 +1,982 @@ -!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(u,a){if(!n[u]){if(!t[u]){var c="function"==typeof require&&require;if(!a&&c)return c(u,!0);if(i)return i(u,!0);var f=new Error("Cannot find module '"+u+"'");throw f.code="MODULE_NOT_FOUND",f}var s=n[u]={exports:{}};t[u][0].call(s.exports,function(e){var n=t[u][1][e];return o(n?n:e)},s,s.exports,e,t,n,r)}return n[u].exports}for(var i="function"==typeof require&&require,u=0;uthis.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,u=r.make(this.type),a=function(){return this.energy-=this.initialEnergy,!0}.bind(this),c=this.wait;return{x:i.x,y:i.y,creature:u,successFn:a,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}):!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 u=e.color;return("object"!=typeof u||3!==u.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 u=t.color;return("object"!=typeof u||3!==u.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){var o=t.getContext("2d");o.clearRect(0,0,t.width,t.height),n.each(e,function(t,e){n.each(t,function(t,n){if(t){var i=t.colorFn?t.colorFn():t.color+","+t.energy/t.maxEnergy;o.fillStyle="rgba("+i+")",t.character?o.fillText(t.character,e*r,n*r+r):o.fillRect(e*r,n*r,r,r)}})})}},{"./util.js":6}],4:[function(t,e){var n=function(t,e,n,r,o){function i(o){var i=document.createElement("canvas"),u=i.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}(),i.width=t*o,i.height=e*o,i.style.width=t+"px",i.style.height=e+"px",u.setTransform(o,0,0,o,0,0),u.font="bold "+n+"px Arial",r&&(i.id=r),i}t*=n,e*=n;var u=i();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,r,o){r=r||10,this.cellSize=r,this.width=t,this.height=e,this.grid=[],this.canvas=u.createCanvasElement(t,e,r,n,o),this.nextFrame=!1}var r=t("./util"),o=t("./creature.js"),i=t("./display.js"),u=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 u=0,a=this.height;a>u;u++)e[r].push(o.make("function"===n?t(r,u):"object"===n&&t.length?(t[u]||[])[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 u=0,a=this.height;a>u;u++)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);return e.age++,e&&!e.isDead()?e:!1}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 u(t,e,n){if(t){var u=r.map(r.getNeighborCoords(e,n,p-1,y-1,t.actionRadius),o),a=t.process(u,e,n);if(a){var c=l[a.x];c[a.y]||(c[a.y]=[]),c[a.y].push({x:e,y:n,creature:a.creature})}else i(t)}}function a(t,e){r.each(t,function(t,n){u(t,e,n)})}function c(t,e,n){if(t){var o=t.splice(r.random(t.length-1),1)[0],u=o.creature;u.successFn()||(s[o.x][o.y]=!1),u.boundEnergy(),s[e][n]=u,r.each(t,i)}}function f(t,e){r.each(t,function(t,n){c(t,e,n)})}var s,l,p=this.width,y=this.height,h=this.grid;for("number"!=typeof t&&(t=1);t--;)h=s?r.clone(s):this.grid,s=r.map(h,n),l=this.makeGrid(),r.each(s,a),r.each(l,f);return s},n.prototype.draw=function(){i(this.canvas,this.grid,this.cellSize)},n.prototype.animate=function(t,e){function n(){o.grid=o.step(),o.draw(),r++!==t?o.nextFrame=requestAnimationFrame(n):(o.nextFrame=!1,e&&e())}if(!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,u,a,c,f=[];("number"!=typeof o||1>o)&&(o=1),i=Math.max(0,t-o),a=Math.max(0,e-o),u=Math.min(t+o,n),c=Math.min(e+o,r);for(var s=i;u>=s;s++)for(var l=a;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,u,a,c){function f(t){var e,n=t.length,o=this,i=0,u=o.i=o.j=0,a=o.S=[];for(n||(t=[n++]);r>i;)a[i]=i++;for(i=0;r>i;i++)a[i]=a[u=m&u+t[i%n]+(e=a[i])],a[u]=e;(o.g=function(t){for(var e,n=0,i=o.i,u=o.j,a=o.S;t--;)e=a[i=m&i+1],n=n*r+a[m&(a[i]=a[u=m&u+e])+(a[u]=e)];return o.i=i,o.j=u,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=v.g(1);for(;t>=d;)t/=2,e/=2,n>>>=1;return(t+n)/e},m,"global"in i?i.global:this==n)};l(n[c](),e),u&&u.exports?u.exports=v:a&&a.amd&&a(function(){return v})}(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 m(t){return"function"==typeof t}function v(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=u.createCallback(e,n,3),we(t)){n=-1;for(var o=t.length;++narguments.length;if(e=u.createCallback(e,r,4),we(t)){var i=-1,a=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=h(Z,Fe,{j:!1}),Oe=h(Z,Fe);m(/x/)&&(m=function(t){return"function"==typeof t&&"[object Function]"==oe.call(t)}),u.assign=ke,u.bind=F,u.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=_e(t),i=o[0],u=t[i];return 1!=o.length||u!==u||v(u)?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],u===t&&(0!==u||1/u==1/t)}},u.filter=x,u.forEach=j,u.forIn=Se,u.forOwn=Oe,u.keys=_e,u.map=w,u.property=O,u.collect=w,u.each=j,u.extend=ke,u.select=x,u.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))},u.identity=C,u.isArguments=d,u.isArray=we,u.isFunction=m,u.isObject=v,u.isString=b,u.noop=S,u.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=me(),de(t+n*(e-t+parseFloat("1e-"+((n+"").length-1))),e)):t+ue(me()*(e-t+1))},u.reduce=E,u.some=_,u.any=_,u.foldl=E,u.inject=E,u.VERSION="2.4.1",X&&Y&&(X._=u)}).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(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.terra=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o this.maxEnergy) + this.energy = this.maxEnergy; + }; + + baseCreature.prototype.isDead = function() { + return this.energy <= 0; + }; + + baseCreature.prototype.reproduce = function (neighbors) { + var spots = _.filter(neighbors, function (spot) { + return !spot.creature; + }); + + if (spots.length) { + var step = spots[_.random(spots.length - 1)]; + var coords = step.coords; + var creature = factory.make(this.type); + + var successFn = (function () { + this.energy -= this.initialEnergy; + return true; + }).bind(this); + var failureFn = this.wait; + + return { + x: coords.x, + y: coords.y, + creature: creature, + successFn: successFn, + failureFn: failureFn + }; + } else return false; + }; + + baseCreature.prototype.move = function (neighbors) { + var creature = this; + + // first, look for creatures to eat + var spots = _.filter(neighbors, (function (spot) { + return spot.creature.size < this.size; + }).bind(this)); + + // if there's not enough food, try to move + if (spots.length < this.sustainability) { + spots = _.filter(neighbors, function (spot) { + return !spot.creature; + }); + } + + // if we've got a spot to move to... + if (spots.length) { + // ...pick one + var step = spots[_.random(spots.length - 1)]; + + var coords = step.coords; + + var successFn = (function () { + var foodEnergy = step.creature.energy * this.efficiency; + // add foodEnergy if eating, subtract 10 if moving + this.energy = this.energy + (foodEnergy || -10); + // clear the original location + return false; + }).bind(this); + + return { + x: coords.x, + y: coords.y, + creature: creature, + successFn: successFn + }; + } else return false; + }; + + baseCreature.prototype.wait = function () { + this.energy -= 5; + return true; + }; + + baseCreature.prototype.process = function (neighbors, x, y) { + var step = {}; + var maxEnergy = this.maxEnergy; + + if (this.energy > maxEnergy * this.reproduceLv && this.reproduce) { + step = this.reproduce(neighbors); + } else if (this.energy > maxEnergy * this.moveLv && this.move) { + step = this.move(neighbors); + } + + var creature = step.creature; + + if (creature) { + creature.successFn = step.successFn || creature.wait; + creature.failureFn = step.failureFn || creature.wait; + + return { + x: step.x, + y: step.y, + creature: creature, + observed: true + }; + } else return this.energy !== this.maxEnergy; + }; + + baseCA.prototype.boundEnergy = function () {}; + baseCA.prototype.isDead = function () { return false; }; + baseCA.prototype.process = function (neighbors, x, y) {}; + baseCA.prototype.wait = function () {}; + + // Storage for our creature types + var types = {}; + + return { + make: function (type, options) { + var Creature = types[type]; + return (Creature ? new Creature(options) : false); + }, + + registerCreature: function (options, init) { + // required attributes + var type = options.type; + // only register classes that fulfill the creature contract + if (typeof type === 'string' && typeof types[type] === 'undefined') { + // set the constructor, including init if it's defined + if (typeof init === 'function') { + types[type] = function () { + this.energy = this.initialEnergy; + init.call(this); + }; + } else { + types[type] = function () { + this.energy = this.initialEnergy; + }; + } + + var color = options.color; + // set the color randomly if none is provided + if (typeof color !== 'object' || color.length !== 3) { + options.color = [_.random(255), _.random(255), _.random(255)]; + } + + types[type].prototype = new baseCreature(); + types[type].prototype.constructor = types[type]; + + _.each(options, function(value, key) { + types[type].prototype[key] = value; + }); + + types[type].prototype.successFn = types[type].wait; + types[type].prototype.failureFn = types[type].wait; + types[type].prototype.energy = options.initialEnergy; + + return true; + } else return false; + }, + + registerCA: function (options, init) { + // required attributes + var type = options.type; + // only register classes that fulfill the creature contract + if (typeof type === 'string' && typeof types[type] === 'undefined') { + // set the constructor, including init if it's defined + types[type] = typeof init === 'function' ? + function () { init.call(this); } : + function () {}; + + var color = options.color; + // set the color randomly if none is provided + if (typeof color !== 'object' || color.length !== 3) { + options.color = [_.random(255), _.random(255), _.random(255)]; + } + + types[type].prototype = new baseCA(); + types[type].prototype.constructor = types[type]; + + _.each(options, function(value, key) { + types[type].prototype[key] = value; + }); + + return true; + } else return false; + } + }; +})(); + +module.exports = factory; + +},{"./util.js":6}],3:[function(require,module,exports){ +var _ = require('./util.js'); + +module.exports = function (canvas, grid, cellSize) { + var ctx = canvas.getContext('2d'); + ctx.clearRect(0, 0, canvas.width, canvas.height); + + _.each(grid, function (column, x) { + _.each(column, function (creature, y) { + if (creature) { + var color = creature.colorFn ? + creature.colorFn() : + creature.color + ',' + creature.energy / creature.maxEnergy; + + ctx.fillStyle = 'rgba(' + color + ')'; + + if (creature.character) { + ctx.fillText(creature.character, x * cellSize, y * cellSize + cellSize); + } else { + ctx.fillRect(x * cellSize, y * cellSize, cellSize, cellSize); + } + } + }); + }); +}; + +},{"./util.js":6}],4:[function(require,module,exports){ +// Creates an HD canvas element on page and +// returns a reference to the element +var createCanvasElement = function (width, height, cellSize, id, insertAfter) { + width *= cellSize; + height *= cellSize; + + // Creates a scaled-up canvas based on the device's + // resolution, then displays it properly using styles + function createHDCanvas (ratio) { + var canvas = document.createElement('canvas'); + var ctx = canvas.getContext('2d'); + + // Creates a dummy canvas to test device's pixel ratio + ratio = (function () { + var ctx = document.createElement('canvas').getContext('2d'); + var dpr = window.devicePixelRatio || 1; + var bsr = ctx.webkitBackingStorePixelRatio || + ctx.mozBackingStorePixelRatio || + ctx.msBackingStorePixelRatio || + ctx.oBackingStorePixelRatio || + ctx.backingStorePixelRatio || 1; + return dpr / bsr; + })(); + + canvas.width = width * ratio; + canvas.height = height * ratio; + canvas.style.width = width + 'px'; + canvas.style.height = height + 'px'; + ctx.setTransform(ratio, 0, 0, ratio, 0, 0); + ctx.font = 'bold ' + cellSize + 'px Arial'; + + if (id) canvas.id = id; + + return canvas; + } + + var canvas = createHDCanvas(); + + if (insertAfter) insertAfter.parentNode.insertBefore(canvas, insertAfter.nextSibling); + else document.body.appendChild(canvas); + + return canvas; +}; + +module.exports = { + createCanvasElement: createCanvasElement +}; + +},{}],5:[function(require,module,exports){ +var _ = require('./util'); +var factory = require('./creature.js'); +var display = require('./display.js'); +var dom = require('./dom.js'); + +/** + * Terrarium constructor function + * @param {int} width number of cells in the x-direction + * @param {int} height number of cells in the y-direction + * @param {string} id id assigned to the generated canvas + * @param {int} cellSize pixel width of each cell (default 10) + * @param {string} insertAfter id of the element to insert the canvas after + */ +function Terrarium(width, height, id, cellSize, insertAfter) { + cellSize = cellSize || 10; + this.cellSize = cellSize; + this.width = width; + this.height = height; + this.grid = []; + this.canvas = dom.createCanvasElement(width, height, cellSize, id, insertAfter); + this.nextFrame = false; + this.hasChanged = false; +} + +/** + * Create a grid and fill it by using a function, 2-d array, or uniform type + * @param {*} content if function, fill grid according to fn(x, y) + * if array, fill grid cells with the corresponding creatureType + * if string, fill grid with that creatureType + * otherwise, create empty grid + * @return {grid} a grid adhering to the above rules + */ +Terrarium.prototype.makeGrid = function (content) { + var grid = [], type = typeof content; + for (var x = 0, _w = this.width; x < _w; x++) { + grid.push([]); + for (var y = 0, _h = this.height; y < _h; y++) { + grid[x].push(factory.make( + type === 'function' ? content(x, y) : + type === 'object' && content.length ? (content[y] || [])[x] : + type === 'string' ? content : + undefined + )); + } + } return grid; +}; + +/** + * Create a grid and fill it randomly with a set creature distribution + * @param {array} distribution an array of arrays of the form [string 'creatureName', float fillPercent] + */ +Terrarium.prototype.makeGridWithDistribution = function (distribution) { + var current, rand = 0, grid = []; + for (var x = 0, _w = this.width; x < _w; x++) { + grid.push([]); + for (var y = 0, _h = this.height; y < _h; y++) { + grid[x].push(factory.make(_.pickRandomWeighted(distribution))); + } + } return grid; +}; + +/** + * Returns the next step of the simulation + * @param {} steps the number of steps to run through before returning + * @return {grid} a new grid after || 1 steps + */ +Terrarium.prototype.step = function (steps) { + function copyAndRemoveInner (origCreature) { + if (origCreature) { + var copy = _.assign(new (origCreature.constructor)(), origCreature); + var dead = copy && copy.isDead(); + if (dead && !self.hasChanged) self.hasChanged = true; + copy.age++; + + return !dead ? copy : false; + } else return false; + } + + function copyAndRemove (origCols) { + return _.map(origCols, copyAndRemoveInner); + } + + // TODO: Switch coords to just x and y to be consistent w/ pickWinnerInner + function zipCoordsWithNeighbors (coords) { + return { + coords: coords, + creature: oldGrid[coords.x][coords.y] + }; + } + + function processLoser (loser) { + var loserCreature = loser.creature; + if (loserCreature) { + loserCreature.failureFn(); + loserCreature.boundEnergy(); + } else { + loser.wait(); + loser.boundEnergy(); + } + } + + function processCreaturesInner (creature, x, y) { + if (creature) { + var neighbors = _.map( + _.getNeighborCoords(x, y, gridWidth - 1, gridHeight - 1, creature.actionRadius), + zipCoordsWithNeighbors + ); + var result = creature.process(neighbors, x, y); + if (typeof result === 'object') { + var eigenColumn = eigenGrid[result.x]; + var returnedCreature = result.creature; + var returnedY = result.y; + + if (!eigenColumn[returnedY]) eigenColumn[returnedY] = []; + + eigenColumn[returnedY].push({ + x: x, + y: y, + creature: returnedCreature + }); + if (!self.hasChanged && returnedCreature.observed) self.hasChanged = true; + } else { + if (result && !self.hasChanged) self.hasChanged = true; + processLoser(creature); + } + } + } + + function processCreatures (column, x) { + _.each(column, function (creature, y) { processCreaturesInner(creature, x, y); }); + } + + function pickWinnerInner (superposition, x, y) { + if (superposition) { + var winner = superposition.splice(_.random(superposition.length - 1), 1)[0]; + var winnerCreature = winner.creature; + + // clear the original creature's square if successFn returns false + if (!winnerCreature.successFn()) { + 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(); + + // put the winner in its rightful place + newGrid[x][y] = winnerCreature; + + // ...and call wait() on the losers. We can do this without + // affecting temporal consistency because all callbacks have + // already been created with prior conditions + _.each(superposition, processLoser); + } + } + + function pickWinner (column, x) { + _.each(column, function (superposition, y) { pickWinnerInner(superposition, x, y); }); + } + + var self = this; + var gridWidth = this.width; + var gridHeight = this.height; + var oldGrid = this.grid, newGrid, eigenGrid; + + if (typeof steps !== 'number') steps = 1; + + while (steps--) { + this.hasChanged = false; + + oldGrid = newGrid ? _.clone(newGrid) : this.grid; + + // copy the old grid & remove dead creatures + newGrid = _.map(oldGrid, copyAndRemove); + + // create an empty grid to hold creatures competing for the same square + eigenGrid = this.makeGrid(); + + // Add each creature's intended destination to the eigenGrid + _.each(newGrid, processCreatures); + + // Choose a winner from each of the eigenGrid's superpositions + _.each(eigenGrid, pickWinner); + + if (!this.hasChanged) return false; + } + + return newGrid; +}; + +/** + * Updates the canvas to reflect the current grid + */ +Terrarium.prototype.draw = function () { + display(this.canvas, this.grid, this.cellSize); +}; + +/** + * Starts animating the simulation. Can be called with only a function. + * @param {int} steps the simulation will stop after steps if specified + * @param {Function} fn called as a callback once the animation finishes + */ +Terrarium.prototype.animate = function (steps, fn) { + function tick () { + var grid = self.step(); + if (grid) { + self.grid = grid; + self.draw(); + if (++i !== steps) return self.nextFrame = requestAnimationFrame(tick); + } // if grid hasn't changed || reached last step + self.nextFrame = false; + if (fn) fn(); + } + + if (typeof steps === 'function') { + fn = steps; + steps = null; + } + + if (!this.nextFrame) { + var i = 0; + var self = this; + self.nextFrame = requestAnimationFrame(tick); + } +}; + +/** + * Stops a currently running animation + */ +Terrarium.prototype.stop = function () { + cancelAnimationFrame(this.nextFrame); + this.nextFrame = false; +}; + +module.exports = Terrarium; + +},{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(require,module,exports){ +// Seed Math.random() with seedrandom +require('../bower_components/seedrandom/seedrandom.js')('terra :)', {global: true}); + +// an extended custom build of lodash, generated with: +// lodash exports=commonjs include=assign,clone,filter,each,map,random,reduce,some +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} 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; + + 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 (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; +}; + +_.pickRandomWeighted = function (weightedArrays) { + var sum = 0, rand = _.random(100, true); + var cur, i; + for (i = 0, _len = weightedArrays.length; i < _len; i++) { + cur = weightedArrays[i]; + sum += cur[1]; + if (sum > rand) return cur[0]; + } return false; +}; + +/** + * CommonJS exports + * @type {Object} + */ +module.exports = _; + +},{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":8}],7:[function(require,module,exports){ +/** + +seedrandom.js +============= + +Seeded random number generator for Javascript. + +version 2.3.6
+Author: David Bau
+Date: 2014 May 14 + +Can be used as a plain script, a node.js module or an AMD module. + +Script tag usage +---------------- + + + +// Sets Math.random to a PRNG initialized using the given explicit seed. +Math.seedrandom('hello.'); +console.log(Math.random()); // Always 0.9282578795792454 +console.log(Math.random()); // Always 0.3752569768646784 + +// Sets Math.random to an ARC4-based PRNG that is autoseeded using the +// current time, dom state, and other accumulated local entropy. +// The generated seed string is returned. +Math.seedrandom(); +console.log(Math.random()); // Reasonably unpredictable. + +// Seeds using the given explicit seed mixed with accumulated entropy. +Math.seedrandom('added entropy.', { entropy: true }); +console.log(Math.random()); // As unpredictable as added entropy. + +// Use "new" to create a local prng without altering Math.random. +var myrng = new Math.seedrandom('hello.'); +console.log(myrng()); // Always 0.9282578795792454 + + +Node.js usage +------------- + +npm install seedrandom + +// Local PRNG: does not affect Math.random. +var seedrandom = require('seedrandom'); +var rng = seedrandom('hello.'); +console.log(rng()); // Always 0.9282578795792454 + +// Autoseeded ARC4-based PRNG. +rng = seedrandom(); +console.log(rng()); // Reasonably unpredictable. + +// Global PRNG: set Math.random. +seedrandom('hello.', { global: true }); +console.log(Math.random()); // Always 0.9282578795792454 + +// Mixing accumulated entropy. +rng = seedrandom('added entropy.', { entropy: true }); +console.log(rng()); // As unpredictable as added entropy. + + +Require.js usage +---------------- + +Similar to node.js usage: + +bower install seedrandom + +require(['seedrandom'], function(seedrandom) { + var rng = seedrandom('hello.'); + console.log(rng()); // Always 0.9282578795792454 +}); + + +Network seeding via a script tag +-------------------------------- + + + + + +Examples of manipulating the seed for various purposes: + +var seed = Math.seedrandom(); // Use prng with an automatic seed. +document.write(Math.random()); // Pretty much unpredictable x. + +var rng = new Math.seedrandom(seed); // A new prng with the same seed. +document.write(rng()); // Repeat the 'unpredictable' x. + +function reseed(event, count) { // Define a custom entropy collector. + var t = []; + function w(e) { + t.push([e.pageX, e.pageY, +new Date]); + if (t.length < count) { return; } + document.removeEventListener(event, w); + Math.seedrandom(t, { entropy: true }); + } + document.addEventListener(event, w); +} +reseed('mousemove', 100); // Reseed after 100 mouse moves. + +The "pass" option can be used to get both the prng and the seed. +The following returns both an autoseeded prng and the seed as an object, +without mutating Math.random: + +var obj = Math.seedrandom(null, { pass: function(prng, seed) { + return { random: prng, seed: seed }; +}}); + + +Version notes +------------- + +The random number sequence is the same as version 1.0 for string seeds. +* Version 2.0 changed the sequence for non-string seeds. +* Version 2.1 speeds seeding and uses window.crypto to autoseed if present. +* Version 2.2 alters non-crypto autoseeding to sweep up entropy from plugins. +* Version 2.3 adds support for "new", module loading, and a null seed arg. +* Version 2.3.1 adds a build environment, module packaging, and tests. +* Version 2.3.4 fixes bugs on IE8, and switches to MIT license. +* Version 2.3.6 adds a readable options object argument. + +The standard ARC4 key scheduler cycles short keys, which means that +seedrandom('ab') is equivalent to seedrandom('abab') and 'ababab'. +Therefore it is a good idea to add a terminator to avoid trivial +equivalences on short string seeds, e.g., Math.seedrandom(str + '\0'). +Starting with version 2.0, a terminator is added automatically for +non-string seeds, so seeding with the number 111 is the same as seeding +with '111\0'. + +When seedrandom() is called with zero args or a null seed, it uses a +seed drawn from the browser crypto object if present. If there is no +crypto support, seedrandom() uses the current time, the native rng, +and a walk of several DOM objects to collect a few bits of entropy. + +Each time the one- or two-argument forms of seedrandom are called, +entropy from the passed seed is accumulated in a pool to help generate +future seeds for the zero- and two-argument forms of seedrandom. + +On speed - This javascript implementation of Math.random() is several +times slower than the built-in Math.random() because it is not native +code, but that is typically fast enough. Some details (timings on +Chrome 25 on a 2010 vintage macbook): + +* seeded Math.random() - avg less than 0.0002 milliseconds per call +* seedrandom('explicit.') - avg less than 0.2 milliseconds per call +* seedrandom('explicit.', true) - avg less than 0.2 milliseconds per call +* seedrandom() with crypto - avg less than 0.2 milliseconds per call + +Autoseeding without crypto is somewhat slower, about 20-30 milliseconds on +a 2012 windows 7 1.5ghz i5 laptop, as seen on Firefox 19, IE 10, and Opera. +Seeded rng calls themselves are fast across these browsers, with slowest +numbers on Opera at about 0.0005 ms per seeded Math.random(). + + +LICENSE (MIT) +------------- + +Copyright (c)2014 David Bau. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +*/ + +/** + * All code is in an anonymous closure to keep the global namespace clean. + */ +(function ( + global, pool, math, width, chunks, digits, module, define, rngname) { + +// +// The following constants are related to IEEE 754 limits. +// +var startdenom = math.pow(width, chunks), + significance = math.pow(2, digits), + overflow = significance * 2, + mask = width - 1, + +// +// seedrandom() +// This is the seedrandom function described above. +// +impl = math['seed' + rngname] = function(seed, options, callback) { + var key = []; + options = (options == true) ? { entropy: true } : (options || {}); + + // Flatten the seed string or build one from local entropy if needed. + var shortseed = mixkey(flatten( + options.entropy ? [seed, tostring(pool)] : + (seed == null) ? autoseed() : seed, 3), key); + + // Use the seed to initialize an ARC4 generator. + var arc4 = new ARC4(key); + + // Mix the randomness into accumulated entropy. + mixkey(tostring(arc4.S), pool); + + // Calling convention: what to return as a function of prng, seed, is_math. + return (options.pass || callback || + // If called as a method of Math (Math.seedrandom()), mutate Math.random + // because that is how seedrandom.js has worked since v1.0. Otherwise, + // it is a newer calling convention, so return the prng directly. + function(prng, seed, is_math_call) { + if (is_math_call) { math[rngname] = prng; return seed; } + else return prng; + })( + + // This function returns a random double in [0, 1) that contains + // randomness in every bit of the mantissa of the IEEE 754 value. + function() { + var n = arc4.g(chunks), // Start with a numerator n < 2 ^ 48 + d = startdenom, // and denominator d = 2 ^ 48. + x = 0; // and no 'extra last byte'. + while (n < significance) { // Fill up all significant digits by + n = (n + x) * width; // shifting numerator and + d *= width; // denominator and generating a + x = arc4.g(1); // new least-significant-byte. + } + while (n >= overflow) { // To avoid rounding up, before adding + n /= 2; // last byte, shift everything + d /= 2; // right using integer math until + x >>>= 1; // we have exactly the desired bits. + } + return (n + x) / d; // Form the number within [0, 1). + }, shortseed, 'global' in options ? options.global : (this == math)); +}; + +// +// ARC4 +// +// An ARC4 implementation. The constructor takes a key in the form of +// an array of at most (width) integers that should be 0 <= x < (width). +// +// The g(count) method returns a pseudorandom integer that concatenates +// the next (count) outputs from ARC4. Its return value is a number x +// that is in the range 0 <= x < (width ^ count). +// +/** @constructor */ +function ARC4(key) { + var t, keylen = key.length, + me = this, i = 0, j = me.i = me.j = 0, s = me.S = []; + + // The empty key [] is treated as [0]. + if (!keylen) { key = [keylen++]; } + + // Set up S using the standard key scheduling algorithm. + while (i < width) { + s[i] = i++; + } + for (i = 0; i < width; i++) { + s[i] = s[j = mask & (j + key[i % keylen] + (t = s[i]))]; + s[j] = t; + } + + // The "g" method returns the next (count) outputs as one number. + (me.g = function(count) { + // Using instance members instead of closure state nearly doubles speed. + var t, r = 0, + i = me.i, j = me.j, s = me.S; + while (count--) { + t = s[i = mask & (i + 1)]; + r = r * width + s[mask & ((s[i] = s[j = mask & (j + t)]) + (s[j] = t))]; + } + me.i = i; me.j = j; + return r; + // For robust unpredictability discard an initial batch of values. + // See http://www.rsa.com/rsalabs/node.asp?id=2009 + })(width); +} + +// +// flatten() +// Converts an object tree to nested arrays of strings. +// +function flatten(obj, depth) { + var result = [], typ = (typeof obj), prop; + if (depth && typ == 'object') { + for (prop in obj) { + try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {} + } + } + return (result.length ? result : typ == 'string' ? obj : obj + '\0'); +} + +// +// mixkey() +// Mixes a string seed into a key that is an array of integers, and +// returns a shortened string seed that is equivalent to the result key. +// +function mixkey(seed, key) { + var stringseed = seed + '', smear, j = 0; + while (j < stringseed.length) { + key[mask & j] = + mask & ((smear ^= key[mask & j] * 19) + stringseed.charCodeAt(j++)); + } + return tostring(key); +} + +// +// autoseed() +// Returns an object for autoseeding, using window.crypto if available. +// +/** @param {Uint8Array|Navigator=} seed */ +function autoseed(seed) { + try { + global.crypto.getRandomValues(seed = new Uint8Array(width)); + return tostring(seed); + } catch (e) { + return [+new Date, global, (seed = global.navigator) && seed.plugins, + global.screen, tostring(pool)]; + } +} + +// +// tostring() +// Converts an array of charcodes to a string +// +function tostring(a) { + return String.fromCharCode.apply(0, a); +} + +// +// When seedrandom.js is loaded, we immediately mix a few bits +// from the built-in RNG into the entropy pool. Because we do +// not want to intefere with determinstic PRNG state later, +// seedrandom will not call math.random on its own again after +// initialization. +// +mixkey(math[rngname](), pool); + +// +// Nodejs and AMD support: export the implemenation as a module using +// either convention. +// +if (module && module.exports) { + module.exports = impl; +} else if (define && define.amd) { + define(function() { return impl; }); +} + +// End anonymous scope, and pass initial values. +})( + this, // global window object + [], // pool: entropy pool starts empty + Math, // math: package containing random, pow, and seedrandom + 256, // width: each RC4 output is 0 <= x < 256 + 6, // chunks: at least six RC4 outputs for each double + 52, // digits: there are 52 significant digits in a double + (typeof module) == 'object' && module, // present in node.js + (typeof define) == 'function' && define, // present with an AMD loader + 'random'// rngname: name for Math.random and Math.seedrandom +); + +},{}],8:[function(require,module,exports){ +(function (global){ +/** + * @license + * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE + * Build: `lodash exports="commonjs" include="assign,clone,filter,each,map,random,reduce,some"` + */ +;(function(){function n(n){return typeof n.toString!="function"&&typeof(n+"")=="string"}function t(n){n.length=0,S.lengthe?0:e);++rk;k++)r+="n='"+e.h[k]+"';if((!(r&&x[n])&&m.call(t,n))",e.j||(r+="||(!x[n]&&t[n]!==A[n])"),r+="){"+e.g+"}";r+="}"}return(e.b||bt.nonEnumArgs)&&(r+="}"),r+=e.c+";return E",n("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",t+r+"}")(c,$,Y,ut,A,g,mt,v,q.f,Z,H,vt,M,nt,tt) +}function s(n){return typeof n=="function"&&et.test(n)}function g(n){return n&&typeof n=="object"&&typeof n.length=="number"&&tt.call(n)==B||false}function y(n){return typeof n=="function"}function h(n){return!(!n||!H[typeof n])}function v(n){return typeof n=="string"||n&&typeof n=="object"&&tt.call(n)==M||false}function b(n,t,e){var o=[];if(t=r.createCallback(t,e,3),mt(n)){e=-1;for(var u=n.length;++earguments.length;if(t=r.createCallback(t,o,4),mt(n)){var a=-1,c=n.length;for(u&&(e=n[++a]);++a3&&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]"}),Ot=p(U,wt,{j:false}),St=p(U,wt); +y(/x/)&&(y=function(n){return typeof n=="function"&&"[object Function]"==tt.call(n)}),r.assign=xt,r.bind=w,r.createCallback=function(n,t,e){var r=typeof n;if(null==n||"function"==r)return c(n,t,e);if("object"!=r)return O(n);var o=Et(n),u=o[0],a=n[u];return 1!=o.length||a!==a||h(a)?function(t){for(var e=o.length,r=false;e--&&(r=l(t[o[e]],n[o[e]],null,true)););return r}:function(n){return n=n[u],a===n&&(0!==a||1/a==1/n)}},r.filter=b,r.forEach=d,r.forIn=Ot,r.forOwn=St,r.keys=Et,r.map=m,r.property=O,r.collect=m,r.each=d,r.extend=xt,r.select=b,r.clone=function(n,t,e,r){return typeof t!="boolean"&&null!=t&&(r=e,e=t,t=false),u(n,t,typeof e=="function"&&c(e,r,1)) +},r.identity=_,r.isArguments=g,r.isArray=mt,r.isFunction=y,r.isObject=h,r.isString=v,r.noop=x,r.random=function(n,t,e){var r=null==n,o=null==t;return null==e&&(typeof n=="boolean"&&o?(e=n,n=1):o||typeof t!="boolean"||(e=t,o=true)),r&&o&&(t=1),n=+n||0,o?(t=n,n=0):t=+t||0,e||n%1||t%1?(e=yt(),gt(n+e*(t-n+parseFloat("1e-"+((e+"").length-1))),t)):n+rt(yt()*(t-n+1))},r.reduce=j,r.some=E,r.any=E,r.foldl=j,r.inject=j,r.VERSION="2.4.1",W&&Q&&(W._=r)}).call(this); +}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) +},{}]},{},[1])(1) +}); \ No newline at end of file diff --git a/gulpfile.js b/gulpfile.js index 1e0e29c..4169253 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -53,7 +53,7 @@ gulp.task('scripts', ['lint'], function() { .pipe(source('terra.js')) .pipe(gulp.dest(paths.dist.scripts)) .pipe($.rename('terra.min.js')) - .pipe($.streamify( $.uglify() )) + //.pipe($.streamify( $.uglify() )) .pipe(gulp.dest(paths.dist.scripts)) }); @@ -76,7 +76,7 @@ gulp.task('sass', function () { gulp.task('js_concat', ['demo'], function () { return gulp.src(paths.demo.extraScripts.concat(paths.demo.temp + '/*.js')) .pipe($.concat('terra.demo.min.js')) - .pipe($.streamify( $.uglify() )) + //.pipe($.streamify( $.uglify() )) .pipe(gulp.dest(paths.dist.demo)) }); diff --git a/package.json b/package.json index ad84b8e..e77b9f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "terra", - "version": "1.0.0-beta", + "version": "1.1.0-beta", "description": "A JavaScript library for simple biological simulations and cellular automata.", "main": "dist/terra.min.js", "scripts": {