diff --git a/index.html b/index.html index 293fce6..ff757fb 100644 --- a/index.html +++ b/index.html @@ -27,7 +27,7 @@
-

terra is a super customizable library for creating and analyzing biological simulations. It's open-source and licenced under MIT.

+

terra is a super customizable library for creating and analyzing biological simulations. It's open-source and licensed under MIT.

Usage

@@ -66,7 +66,7 @@ window.terra;

Creating the environment

To run a simulation, we'll need to create an environment. Let's make a 25x25 grid, populate 10% of the space with our lonely purple creature, and fill the rest with simple plants.

-
var ex1 = new terra.Terrarium(25, 25, 'ex1');
+    
var ex1 = new terra.Terrarium(25, 25, {id: 'ex1'});
 ex1.grid = ex1.makeGridWithDistribution([['secondCreature', 10], ['simplePlant', 90]]);

Running a simulation

@@ -79,7 +79,10 @@ ex1.grid = ex1.makeGridWithDistribution([['secondCreature', 10], ['simplePlant',

Examples

Conway's Game of Life ?

-
var gameOfLife = new terra.Terrarium(25, 25);
+    
var gameOfLife = new terra.Terrarium(25, 25, {
+  trails: 0.9,
+  background: [22, 22, 22]
+});
 
 terra.registerCA({
   type: 'GoL',
@@ -342,9 +345,13 @@ elementary.animate();

Terrarium

Terrariums are where the action happens. They're initialized with the following constructor:

-
//new terra.Terrarium(width, height, id, cellSize, insertAfter);
+    
//new terra.Terrarium(width, height, {options});
 //example: create a 4x4 terrarium called #myTerrarium after element #bugStory
-var t = new terra.Terrarium(4, 4, 'myTerrarium', 15, document.getElementById('bugStory');
+var t = new terra.Terrarium(4, 4, { + id: 'myTerrarium', + cellSize: 15, + insertAfter: document.getElementById('bugStory') +});

Required

  • @@ -376,6 +383,23 @@ var t = new terra.Terrarium(4, 4, 'myTerrarium', 15, document.getElementById('bu
  • Default: canvas is appended to document.body
+
  • +

    float trails

    +

    Allows for "trails", which visualize system history. A value of 1 shows all past state; trails fade faster as we approach 0.

    +
      +
    • Range: [0, 1]
    • +
    • Default: canvas is appended to document.body
    • +
    • Dependencies: "background" option is required if trails is set.
    • +
    +
  • +
  • +

    int [3] background

    +

    RGB components of the canvas' background.

    +
      +
    • Range: [0, 255]
    • +
    • Default: transparent
    • +
    +
  • Once initialized, terrariums have a few exposed methods. Using our terrarium t that we just created:

    diff --git a/terra.demo.min.js b/terra.demo.min.js index 5f7e660..c46f468 100755 --- a/terra.demo.min.js +++ b/terra.demo.min.js @@ -1,1212 +1,2 @@ -/** smooth-scroll v5.0.4, by Chris Ferdinandi | http://github.com/cferdinandi/smooth-scroll | Licensed under MIT: http://gomakethings.com/mit/ */ -Function.prototype.bind||(Function.prototype.bind=function(t){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var o=Array.prototype.slice.call(arguments,1),n=this;return fNOP=function(){},fBound=function(){return n.apply(this instanceof fNOP&&t?this:t,o.concat(Array.prototype.slice.call(arguments)))},fNOP.prototype=this.prototype,fBound.prototype=new fNOP,fBound}); -/** smooth-scroll v5.0.4, by Chris Ferdinandi | http://github.com/cferdinandi/smooth-scroll | Licensed under MIT: http://gomakethings.com/mit/ */ -!function(e,t){"function"==typeof define&&define.amd?define("smoothScroll",t(e)):"object"==typeof exports?module.exports=t(e):e.smoothScroll=t(e)}(this,function(e){"use strict";var t,n={},o=!!document.querySelector&&!!e.addEventListener,a={speed:500,easing:"easeInOutCubic",offset:0,updateURL:!0,callbackBefore:function(){},callbackAfter:function(){}},r=function(e,t,n){if("[object Object]"===Object.prototype.toString.call(e))for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&t.call(n,e[o],o,e);else for(var a=0,r=e.length;r>a;a++)t.call(n,e[a],a,e)},c=function(e,t){var n={};return r(e,function(t,o){n[o]=e[o]}),r(t,function(e,o){n[o]=t[o]}),n},u=function(e){for(var t,n=String(e),o=n.length,a=-1,r="",c=n.charCodeAt(0);++a=1&&31>=t||127==t||0===a&&t>=48&&57>=t||1===a&&t>=48&&57>=t&&45===c?"\\"+t.toString(16)+" ":t>=128||45===t||95===t||t>=48&&57>=t||t>=65&&90>=t||t>=97&&122>=t?n.charAt(a):"\\"+n.charAt(a)}return r},i=function(e,t){var n;return"easeInQuad"===e&&(n=t*t),"easeOutQuad"===e&&(n=t*(2-t)),"easeInOutQuad"===e&&(n=.5>t?2*t*t:-1+(4-2*t)*t),"easeInCubic"===e&&(n=t*t*t),"easeOutCubic"===e&&(n=--t*t*t+1),"easeInOutCubic"===e&&(n=.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1),"easeInQuart"===e&&(n=t*t*t*t),"easeOutQuart"===e&&(n=1- --t*t*t*t),"easeInOutQuart"===e&&(n=.5>t?8*t*t*t*t:1-8*--t*t*t*t),"easeInQuint"===e&&(n=t*t*t*t*t),"easeOutQuint"===e&&(n=1+--t*t*t*t*t),"easeInOutQuint"===e&&(n=.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t),n||t},l=function(e,t,n){var o=0;if(e.offsetParent)do o+=e.offsetTop,e=e.offsetParent;while(e);return o=o-t-n,o>=0?o:0},f=function(){return Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},s=function(e){return e&&"object"==typeof JSON&&"function"==typeof JSON.parse?JSON.parse(e):{}},d=function(e,t){history.pushState&&(t||"true"===t)&&history.pushState({pos:e.id},"",window.location.pathname+e)};return n.animateScroll=function(t,n,o,r){var h=c(h||a,o||{}),p=s(t?t.getAttribute("data-options"):null);h=c(h,p),n="#"+u(n.substr(1));var m,b,g,v=document.querySelector("[data-scroll-header]"),O=null===v?0:v.offsetHeight+v.offsetTop,y=e.pageYOffset,S=l(document.querySelector(n),O,parseInt(h.offset,10)),I=S-y,Q=f(),A=0;t&&"a"===t.tagName.toLowerCase()&&r&&r.preventDefault(),d(n,h.updateURL);var C=function(o,a,r){var c=e.pageYOffset;(o==a||c==a||e.innerHeight+c>=Q)&&(clearInterval(r),h.callbackAfter(t,n))},H=function(){A+=16,b=A/parseInt(h.speed,10),b=b>1?1:b,g=y+I*i(h.easing,b),e.scrollTo(0,Math.floor(g)),C(g,S,m)},w=function(){h.callbackBefore(t,n),m=setInterval(H,16)};0===e.pageYOffset&&e.scrollTo(0,0),w()},n.init=function(e){if(o){t=c(a,e||{});var u=document.querySelectorAll("[data-scroll]");r(u,function(e){e.addEventListener("click",n.animateScroll.bind(null,e,e.hash,t),!1)})}},n}); -/* http://prismjs.com/download.html?themes=prism&languages=markup+clike+javascript+bash */ -self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content)):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof r)){c.lastIndex=0;var p=c.exec(d);if(p){u&&(f=p[1].length);var m=p.index-1+f,p=p[0].slice(f),v=p.length,y=m+v,k=d.slice(0,m+1),b=d.slice(y+1),w=[h,1];k&&w.push(k);var N=new r(o,g?t.tokenize(p,g):p);w.push(N),b&&w.push(b),Array.prototype.splice.apply(a,w)}}}}}return a},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(r&&r.length)for(var a,i=0;a=r[i++];)a(n)}}},n=t.Token=function(e,t){this.type=e,this.content=t};if(n.stringify=function(e,r,a){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,r,e)}).join("");var i={type:e.type,content:n.stringify(e.content,r,a),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:a};"comment"==i.type&&(i.attributes.spellcheck="true"),t.hooks.run("wrap",i);var o="";for(var l in i.attributes)o+=l+'="'+(i.attributes[l]||"")+'"';return"<"+i.tag+' class="'+i.classes.join(" ")+'" '+o+">"+i.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,a=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(a,t.languages[r])))),self.close()},!1),self.Prism):self.Prism;var r=document.getElementsByTagName("script");return r=r[r.length-1],r&&(t.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism);; -Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/\&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(t){"entity"===t.type&&(t.attributes.title=t.content.replace(/&/,"&"))});; -Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g};; -Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}});; -Prism.languages.bash=Prism.languages.extend("clike",{comment:{pattern:/(^|[^"{\\])(#.*?(\r?\n|$))/g,lookbehind:!0},string:{pattern:/("|')(\\?[\s\S])*?\1/g,inside:{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g}},keyword:/\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g}),Prism.languages.insertBefore("bash","keyword",{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g}),Prism.languages.insertBefore("bash","comment",{important:/(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g});; -(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){ -(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o), -m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) -})(window,document,'script','//www.google-analytics.com/analytics.js','ga'); - -ga('create', 'UA-49939229-4', 'auto'); -ga('send', 'pageview'); -(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= height && !isSticky) { - document.body.className = 'sticky'; - topTerrarium.stop(); - isSticky = true; - } else if (scrollTop < height && isSticky) { - document.body.className = ''; - topTerrarium.animate(); - isSticky = false; - } -}; - -function addPlayButton (terrarium, steps) { - var canvas = terrarium.canvas; - var parent = canvas.parentNode; - var sibling = canvas.nextElementSibling; - var wrapper = document.createElement('div'); - var playButton = document.createElement('button'); - var stopButton = document.createElement('button'); - playButton.className = 'icon-play'; - stopButton.className = 'icon-stop'; - wrapper.className = 'canvasWrapper stopped'; - wrapper.appendChild(canvas); - wrapper.appendChild(playButton); - wrapper.appendChild(stopButton); - if (sibling) parent.insertBefore(wrapper, sibling); - else parent.appendChild(wrapper); - - playButton.addEventListener('click', function () { - wrapper.className = 'canvasWrapper playing' - terrarium.animate(steps, function () { stopButton.click(); }); - }, false); - - stopButton.addEventListener('click', function () { - wrapper.className = 'canvasWrapper stopped' - terrarium.stop(); - terrarium.grid = terrarium.distributed ? - terrarium.makeGridWithDistribution(terrarium.initial) : - terrarium.makeGrid(terrarium.initial); - }, false); -}; - -var scroller1 = document.getElementById('scroller1'); -var header = document.getElementById('header'); -var golExample = document.getElementById('gol'); -var cyclicExample = document.getElementById('cyclic'); -var bbExample = document.getElementById('brutesAndBullies'); -var elementaryExample = document.getElementById('rule146'); -var ex1End = document.getElementById('ex1End'); - -var height = window.innerHeight; -var width = document.documentElement.clientWidth; -var terHeight = Math.ceil(height / 20); -var terWidth = Math.ceil(width / 20); - -var isSticky = false; - -var topTerrarium = new Terrarium(terWidth, terHeight, 'topTerrarium', 20, header); -topTerrarium.canvas.style.top = topTerrarium.canvas.style.left = '50%'; -topTerrarium.canvas.style.marginTop = terHeight * 20 / -2 + 'px'; -topTerrarium.canvas.style.marginLeft = terWidth * 20 / -2 + 'px'; -topTerrarium.initial = [['plant', 50], ['brute', 5], ['bully', 10]]; -topTerrarium.distributed = true; - -var ex1Terrarium = new Terrarium(25, 25, 'ex1', 12, ex1End); -ex1Terrarium.initial = [['secondCreature', 10], ['simplePlant', 90]]; -ex1Terrarium.distributed = true; -ex1Terrarium.grid = ex1Terrarium.makeGridWithDistribution(ex1Terrarium.initial); - -var golTerrarium = new Terrarium(25, 25, 'golTerrarium', 12, golExample); -golTerrarium.initial = 'GoL'; -golTerrarium.distributed = false; -golTerrarium.grid = golTerrarium.makeGrid(golTerrarium.initial); - -var cyclicTerrarium = new Terrarium(100, 100, 'cyclicTerrarium', 3, cyclicExample); -cyclicTerrarium.initial = 'cyclic'; -cyclicTerrarium.distributed = false; -cyclicTerrarium.grid = cyclicTerrarium.makeGrid(cyclicTerrarium.initial); - -var elementaryTerrarium = new Terrarium(150, 150, 'elementaryTerrarium', 2, elementaryExample); -elementaryTerrarium.initial = 'elementary'; -elementaryTerrarium.distributed = false; -elementaryTerrarium.grid = elementaryTerrarium.makeGrid(elementaryTerrarium.initial); - -var bbTerrarium = new Terrarium(50, 50, 'bbTerrarium', 6, bbExample); -bbTerrarium.initial = topTerrarium.initial; -bbTerrarium.distributed = true; -bbTerrarium.grid = bbTerrarium.makeGridWithDistribution(bbTerrarium.initial); - -topTerrarium.draw(); -ex1Terrarium.draw(); -golTerrarium.draw(); -cyclicTerrarium.draw(); -bbTerrarium.draw(); - -(function endless () { - topTerrarium.grid = topTerrarium.makeGridWithDistribution(topTerrarium.initial); - topTerrarium.animate(function() { - setTimeout(endless, 400); - }); -})(); -addPlayButton(ex1Terrarium, 300); -addPlayButton(golTerrarium, 300); -addPlayButton(cyclicTerrarium); -addPlayButton(elementaryTerrarium, 150); -addPlayButton(bbTerrarium); - -smoothScroll.init({offset: 60}); - -document.addEventListener('scroll', checkScroll); -scroller1.addEventListener('click', function () { topTerrarium.stop(); }, false); - -},{"../../app/terrarium.js":5,"./bugs.js":8}],2:[function(require,module,exports){ -var _ = require('./util.js'); - -// abstract factory that adds a superclass of baseCreature -var factory = (function () { - function baseCreature() { - this.age = -1; - } - function baseCA() { - this.age = -1; - } - - baseCreature.prototype.initialEnergy = 50; - baseCreature.prototype.maxEnergy = 100; - baseCreature.prototype.efficiency = 0.7; - baseCreature.prototype.size = 50; - baseCreature.prototype.actionRadius = 1; - baseCreature.prototype.sustainability = 2; - // used as percentages of maxEnergy - baseCreature.prototype.reproduceLv = 0.70; - baseCreature.prototype.moveLv = 0; - - baseCreature.prototype.boundEnergy = function() { - if (this.energy > 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":9}],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){ -var factory = require('../../app/creature.js'); -var _ = require('../../app/util.js'); - -factory.registerCreature({ - type: 'plant', - color: [0, 120, 0], - size: 10, - initialEnergy: 5, - maxEnergy: 20, - wait: function() { - this.energy += 1; - }, - move: false, - reproduceLv: 0.65 -}); - -factory.registerCreature({ - type: 'brute', - color: [0, 255, 255], - maxEnergy: 50, - initialEnergy: 10, - size: 20 -}); - -factory.registerCreature({ - type: 'bully', - color: [241, 196, 15], - initialEnergy: 20, - reproduceLv: 0.6, - sustainability: 3 -}); - -factory.registerCA({ - type: 'GoL', - colorFn: function () { return this.alive ? this.color + ',1' : '0,0,0,0'; }, - process: function (neighbors) { - var surrounding = _.filter(neighbors, function (spot) { - return spot.creature.alive; - }).length; - this.alive = surrounding === 3 || surrounding === 2 && this.alive; - return true; - } -}, function () { - this.alive = Math.random() < 0.5; -}); - -factory.registerCA({ - type: 'cyclic', - colors: ['255,0,0,1', '255,96,0,1', '255,191,0,1', '223,255,0,1', '128,255,0,1', '32,255,0,1', '0,255,64,1', '0,255,159,1', '0,255,255,1', '0,159,255,1', '0,64,255,1', '32,0,255,1', '127,0,255,1', '223,0,255,1', '255,0,191,1', '255,0,96,1'], - colorFn: function () { return this.colors[this.state];}, - process: function (neighbors) { - var next = (this.state + 1) % 16; - var changing = _.some(neighbors, function (spot) { - return spot.creature.state === next; - }); - if (changing) this.state = next; - return true; - } -}, function () { - this.state = Math.floor(Math.random() * 16); -}); - -factory.registerCreature({ - type: 'secondCreature', - color: [154, 109, 235], - initialEnergy: 100, - sustainability: 6, - reproduceLv: 1, - moveLv: 0 -}); - -factory.registerCreature({ - type: 'simplePlant', - color: [166, 226, 46], - colorFn: function () { - return this.color + ',1'; - }, - size: 10, - initialEnergy: 5, - maxEnergy: 20, - wait: function() { - this.energy += 1; - }, - move: false, - reproduceLv: 0.65 -}); - -factory.registerCA({ - type: 'elementary', - alive: false, - ruleset: [1, 0, 0, 1, 0, 0, 1, 0].reverse(), // rule 146 - colorFn: function () { return this.alive ? this.color + ',1' : '0,0,0,0'; }, - process: function (neighbors, x, y) { - if (this.age === y) { - var index = _.map(_.filter(neighbors, function (neighbor) { - return neighbor.coords.y === y - 1; - }), function (neighbor) { - return neighbor.creature.alive ? 1 : 0; - }); - index = parseInt(index.join(''), 2); - this.alive = isNaN(index) ? !x : this.ruleset[index]; - } - return true; - } -}); - -},{"../../app/creature.js":2,"../../app/util.js":6}],9:[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]); +Function.prototype.bind||(Function.prototype.bind=function(e){if("function"!=typeof this)throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");var t=Array.prototype.slice.call(arguments,1),n=this;return fNOP=function(){},fBound=function(){return n.apply(this instanceof fNOP&&e?this:e,t.concat(Array.prototype.slice.call(arguments)))},fNOP.prototype=this.prototype,fBound.prototype=new fNOP,fBound}),!function(e,t){"function"==typeof define&&define.amd?define("smoothScroll",t(e)):"object"==typeof exports?module.exports=t(e):e.smoothScroll=t(e)}(this,function(e){"use strict";var t,n={},r=!!document.querySelector&&!!e.addEventListener,i={speed:500,easing:"easeInOutCubic",offset:0,updateURL:!0,callbackBefore:function(){},callbackAfter:function(){}},o=function(e,t,n){if("[object Object]"===Object.prototype.toString.call(e))for(var r in e)Object.prototype.hasOwnProperty.call(e,r)&&t.call(n,e[r],r,e);else for(var i=0,o=e.length;o>i;i++)t.call(n,e[i],i,e)},a=function(e,t){var n={};return o(e,function(t,r){n[r]=e[r]}),o(t,function(e,r){n[r]=t[r]}),n},u=function(e){for(var t,n=String(e),r=n.length,i=-1,o="",a=n.charCodeAt(0);++i=1&&31>=t||127==t||0===i&&t>=48&&57>=t||1===i&&t>=48&&57>=t&&45===a?"\\"+t.toString(16)+" ":t>=128||45===t||95===t||t>=48&&57>=t||t>=65&&90>=t||t>=97&&122>=t?n.charAt(i):"\\"+n.charAt(i)}return o},c=function(e,t){var n;return"easeInQuad"===e&&(n=t*t),"easeOutQuad"===e&&(n=t*(2-t)),"easeInOutQuad"===e&&(n=.5>t?2*t*t:-1+(4-2*t)*t),"easeInCubic"===e&&(n=t*t*t),"easeOutCubic"===e&&(n=--t*t*t+1),"easeInOutCubic"===e&&(n=.5>t?4*t*t*t:(t-1)*(2*t-2)*(2*t-2)+1),"easeInQuart"===e&&(n=t*t*t*t),"easeOutQuart"===e&&(n=1- --t*t*t*t),"easeInOutQuart"===e&&(n=.5>t?8*t*t*t*t:1-8*--t*t*t*t),"easeInQuint"===e&&(n=t*t*t*t*t),"easeOutQuint"===e&&(n=1+--t*t*t*t*t),"easeInOutQuint"===e&&(n=.5>t?16*t*t*t*t*t:1+16*--t*t*t*t*t),n||t},s=function(e,t,n){var r=0;if(e.offsetParent)do r+=e.offsetTop,e=e.offsetParent;while(e);return r=r-t-n,r>=0?r:0},l=function(){return Math.max(document.body.scrollHeight,document.documentElement.scrollHeight,document.body.offsetHeight,document.documentElement.offsetHeight,document.body.clientHeight,document.documentElement.clientHeight)},f=function(e){return e&&"object"==typeof JSON&&"function"==typeof JSON.parse?JSON.parse(e):{}},p=function(e,t){history.pushState&&(t||"true"===t)&&history.pushState({pos:e.id},"",window.location.pathname+e)};return n.animateScroll=function(t,n,r,o){var g=a(g||i,r||{}),d=f(t?t.getAttribute("data-options"):null);g=a(g,d),n="#"+u(n.substr(1));var h,y,m,v=document.querySelector("[data-scroll-header]"),b=null===v?0:v.offsetHeight+v.offsetTop,w=e.pageYOffset,k=s(document.querySelector(n),b,parseInt(g.offset,10)),x=k-w,E=l(),j=0;t&&"a"===t.tagName.toLowerCase()&&o&&o.preventDefault(),p(n,g.updateURL);var C=function(r,i,o){var a=e.pageYOffset;(r==i||a==i||e.innerHeight+a>=E)&&(clearInterval(o),g.callbackAfter(t,n))},S=function(){j+=16,y=j/parseInt(g.speed,10),y=y>1?1:y,m=w+x*c(g.easing,y),e.scrollTo(0,Math.floor(m)),C(m,k,h)},O=function(){g.callbackBefore(t,n),h=setInterval(S,16)};0===e.pageYOffset&&e.scrollTo(0,0),O()},n.init=function(e){if(r){t=a(i,e||{});var u=document.querySelectorAll("[data-scroll]");o(u,function(e){e.addEventListener("click",n.animateScroll.bind(null,e,e.hash,t),!1)})}},n}),self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{};var Prism=function(){var e=/\blang(?:uage)?-(?!\*)(\w+)\b/i,t=self.Prism={util:{encode:function(e){return e instanceof n?new n(e.type,t.util.encode(e.content)):"Array"===t.util.type(e)?e.map(t.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(d instanceof r)){s.lastIndex=0;var h=s.exec(d);if(h){f&&(p=h[1].length);var y=h.index-1+p,h=h[0].slice(p),m=h.length,v=y+m,b=d.slice(0,y+1),w=d.slice(v+1),k=[g,1];b&&k.push(b);var x=new r(a,l?t.tokenize(h,l):h);k.push(x),w&&k.push(w),Array.prototype.splice.apply(i,k)}}}}}return i},hooks:{all:{},add:function(e,n){var r=t.hooks.all;r[e]=r[e]||[],r[e].push(n)},run:function(e,n){var r=t.hooks.all[e];if(r&&r.length)for(var i,o=0;i=r[o++];)i(n)}}},n=t.Token=function(e,t){this.type=e,this.content=t};if(n.stringify=function(e,r,i){if("string"==typeof e)return e;if("[object Array]"==Object.prototype.toString.call(e))return e.map(function(t){return n.stringify(t,r,e)}).join("");var o={type:e.type,content:n.stringify(e.content,r,i),tag:"span",classes:["token",e.type],attributes:{},language:r,parent:i};"comment"==o.type&&(o.attributes.spellcheck="true"),t.hooks.run("wrap",o);var a="";for(var u in o.attributes)a+=u+'="'+(o.attributes[u]||"")+'"';return"<"+o.tag+' class="'+o.classes.join(" ")+'" '+a+">"+o.content+""},!self.document)return self.addEventListener?(self.addEventListener("message",function(e){var n=JSON.parse(e.data),r=n.language,i=n.code;self.postMessage(JSON.stringify(t.util.encode(t.tokenize(i,t.languages[r])))),self.close()},!1),self.Prism):self.Prism;var r=document.getElementsByTagName("script");return r=r[r.length-1],r&&(t.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",t.highlightAll)),self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),Prism.languages.markup={comment://g,prolog:/<\?.+?\?>/,doctype://,cdata://i,tag:{pattern:/<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|[^\s'">=]+))?\s*)*\/?>/gi,inside:{tag:{pattern:/^<\/?[\w:-]+/i,inside:{punctuation:/^<\/?/,namespace:/^[\w-]+?:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi,inside:{punctuation:/=|>|"/g}},punctuation:/\/?>/g,"attr-name":{pattern:/[\w:-]+/g,inside:{namespace:/^[\w-]+?:/}}}},entity:/\&#?[\da-z]{1,8};/gi},Prism.hooks.add("wrap",function(e){"entity"===e.type&&(e.attributes.title=e.content.replace(/&/,"&"))}),Prism.languages.clike={comment:[{pattern:/(^|[^\\])\/\*[\w\W]*?\*\//g,lookbehind:!0},{pattern:/(^|[^\\:])\/\/.*?(\r?\n|$)/g,lookbehind:!0}],string:/("|')(\\?.)*?\1/g,"class-name":{pattern:/((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/gi,lookbehind:!0,inside:{punctuation:/(\.|\\)/}},keyword:/\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g,"boolean":/\b(true|false)\b/g,"function":{pattern:/[a-z0-9_]+\(/gi,inside:{punctuation:/\(/}},number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g,operator:/[-+]{1,2}|!|<=?|>=?|={1,3}|&{1,2}|\|?\||\?|\*|\/|\~|\^|\%/g,ignore:/&(lt|gt|amp);/gi,punctuation:/[{}[\];(),.:]/g},Prism.languages.javascript=Prism.languages.extend("clike",{keyword:/\b(break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|false|finally|for|function|get|if|implements|import|in|instanceof|interface|let|new|null|package|private|protected|public|return|set|static|super|switch|this|throw|true|try|typeof|var|void|while|with|yield)\b/g,number:/\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g}),Prism.languages.insertBefore("javascript","keyword",{regex:{pattern:/(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g,lookbehind:!0}}),Prism.languages.markup&&Prism.languages.insertBefore("markup","tag",{script:{pattern:/[\w\W]*?<\/script>/gi,inside:{tag:{pattern:/|<\/script>/gi,inside:Prism.languages.markup.tag.inside},rest:Prism.languages.javascript}}}),Prism.languages.bash=Prism.languages.extend("clike",{comment:{pattern:/(^|[^"{\\])(#.*?(\r?\n|$))/g,lookbehind:!0},string:{pattern:/("|')(\\?[\s\S])*?\1/g,inside:{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g}},keyword:/\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g}),Prism.languages.insertBefore("bash","keyword",{property:/\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g}),Prism.languages.insertBefore("bash","comment",{important:/(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g}),function(e,t,n,r,i,o,a){e.GoogleAnalyticsObject=i,e[i]=e[i]||function(){(e[i].q=e[i].q||[]).push(arguments)},e[i].l=1*new Date,o=t.createElement(n),a=t.getElementsByTagName(n)[0],o.async=1,o.src=r,a.parentNode.insertBefore(o,a)}(window,document,"script","//www.google-analytics.com/analytics.js","ga"),ga("create","UA-49939229-4","auto"),ga("send","pageview"),function e(t,n,r){function i(a,u){if(!n[a]){if(!t[a]){var c="function"==typeof require&&require;if(!u&&c)return c(a,!0);if(o)return o(a,!0);var s=new Error("Cannot find module '"+a+"'");throw s.code="MODULE_NOT_FOUND",s}var l=n[a]={exports:{}};t[a][0].call(l.exports,function(e){var n=t[a][1][e];return i(n?n:e)},l,l.exports,e,t,n,r)}return n[a].exports}for(var o="function"==typeof require&&require,a=0;a=f&&!h?(document.body.className="sticky",y.stop(),h=!0):f>e&&h&&(document.body.className="",y.animate(),h=!1)}function n(e,t){var n=e.canvas,r=n.parentNode,i=n.nextElementSibling,o=document.createElement("div"),a=document.createElement("button"),u=document.createElement("button");a.className="icon-play",u.className="icon-stop",o.className="canvasWrapper stopped",o.appendChild(n),o.appendChild(a),o.appendChild(u),i?r.insertBefore(o,i):r.appendChild(o),a.addEventListener("click",function(){o.className="canvasWrapper playing",e.animate(t,function(){u.click()})},!1),u.addEventListener("click",function(){o.className="canvasWrapper stopped",e.stop(),e.grid=e.distributed?e.makeGridWithDistribution(e.initial):e.makeGrid(e.initial)},!1)}var r=e("../../app/terrarium.js");e("./bugs.js");var i=document.getElementById("scroller1"),o=document.getElementById("header"),a=document.getElementById("gol"),u=document.getElementById("cyclic"),c=document.getElementById("brutesAndBullies"),s=document.getElementById("rule146"),l=document.getElementById("ex1End"),f=window.innerHeight,p=document.documentElement.clientWidth,g=Math.ceil(f/20),d=Math.ceil(p/20),h=!1,y=new r(d,g,{id:"topTerrarium",cellSize:20,insertAfter:o});y.canvas.style.top=y.canvas.style.left="50%",y.canvas.style.marginTop=20*g/-2+"px",y.canvas.style.marginLeft=20*d/-2+"px",y.initial=[["plant",50],["brute",5],["bully",10]],y.distributed=!0;var m=new r(25,25,{id:"ex1",cellSize:12,insertAfter:l});m.initial=[["secondCreature",10],["simplePlant",90]],m.distributed=!0,m.grid=m.makeGridWithDistribution(m.initial);var v=new r(25,25,{id:"golTerrarium",cellSize:12,insertAfter:a,background:[22,22,22],trails:.9});v.initial="GoL",v.distributed=!1,v.grid=v.makeGrid(v.initial);var b=new r(100,100,{id:"cyclicTerrarium",cellSize:3,insertAfter:u});b.initial="cyclic",b.distributed=!1,b.grid=b.makeGrid(b.initial);var w=new r(150,150,{id:"elementaryTerrarium",cellSize:2,insertAfter:s});w.initial="elementary",w.distributed=!1,w.grid=w.makeGrid(w.initial);var k=new r(50,50,{id:"bbTerrarium",cellSize:6,insertAfter:c});k.initial=y.initial,k.distributed=!0,k.grid=k.makeGridWithDistribution(k.initial),y.draw(),m.draw(),v.draw(),b.draw(),k.draw(),function x(){y.grid=y.makeGridWithDistribution(y.initial),y.animate(function(){setTimeout(x,400)})}(),n(m,300),n(v,300),n(b),n(w,150),n(k),smoothScroll.init({offset:60}),document.addEventListener("scroll",t),i.addEventListener("click",function(){y.stop()},!1)},{"../../app/terrarium.js":5,"./bugs.js":8}],2:[function(e,t){var n=e("./util.js"),r=function(){function e(){this.age=-1}function t(){this.age=-1}e.prototype.initialEnergy=50,e.prototype.maxEnergy=100,e.prototype.efficiency=.7,e.prototype.size=50,e.prototype.actionRadius=1,e.prototype.sustainability=2,e.prototype.reproduceLv=.7,e.prototype.moveLv=0,e.prototype.boundEnergy=function(){this.energy>this.maxEnergy&&(this.energy=this.maxEnergy)},e.prototype.isDead=function(){return this.energy<=0},e.prototype.reproduce=function(e){var t=n.filter(e,function(e){return!e.creature});if(t.length){var i=t[n.random(t.length-1)],o=i.coords,a=r.make(this.type),u=function(){return this.energy-=this.initialEnergy,!0}.bind(this),c=this.wait;return{x:o.x,y:o.y,creature:a,successFn:u,failureFn:c}}return!1},e.prototype.move=function(e){var t=this,r=n.filter(e,function(e){return e.creature.sizen*this.reproduceLv&&this.reproduce?t=this.reproduce(e):this.energy>n*this.moveLv&&this.move&&(t=this.move(e));var r=t.creature;return r?(r.successFn=t.successFn||r.wait,r.failureFn=t.failureFn||r.wait,{x:t.x,y:t.y,creature:r,observed:!0}):this.energy!==this.maxEnergy},t.prototype.boundEnergy=function(){},t.prototype.isDead=function(){return!1},t.prototype.process=function(){},t.prototype.wait=function(){};var i={};return{make:function(e,t){var n=i[e];return n?new n(t):!1},registerCreature:function(t,r){var o=t.type;if("string"==typeof o&&"undefined"==typeof i[o]){i[o]="function"==typeof r?function(){this.energy=this.initialEnergy,r.call(this)}:function(){this.energy=this.initialEnergy};var a=t.color;return("object"!=typeof a||3!==a.length)&&(t.color=[n.random(255),n.random(255),n.random(255)]),i[o].prototype=new e,i[o].prototype.constructor=i[o],n.each(t,function(e,t){i[o].prototype[t]=e}),i[o].prototype.successFn=i[o].wait,i[o].prototype.failureFn=i[o].wait,i[o].prototype.energy=t.initialEnergy,!0}return!1},registerCA:function(e,r){var o=e.type;if("string"==typeof o&&"undefined"==typeof i[o]){i[o]="function"==typeof r?function(){r.call(this)}:function(){};var a=e.color;return("object"!=typeof a||3!==a.length)&&(e.color=[n.random(255),n.random(255),n.random(255)]),i[o].prototype=new t,i[o].prototype.constructor=i[o],n.each(e,function(e,t){i[o].prototype[t]=e}),!0}return!1}}}();t.exports=r},{"./util.js":6}],3:[function(e,t){var n=e("./util.js");t.exports=function(e,t,r,i,o){var a=e.getContext("2d");if(i&&o)a.fillStyle="rgba("+o+","+(1-i)+")",a.fillRect(0,0,e.width,e.height);else{if(i)throw"Background must also be set for trails";a.clearRect(0,0,e.width,e.height)}n.each(t,function(e,t){n.each(e,function(e,n){if(e){var i=e.colorFn?e.colorFn():e.color+","+e.energy/e.maxEnergy;a.fillStyle="rgba("+i+")",e.character?a.fillText(e.character,t*r,n*r+r):a.fillRect(t*r,n*r,r,r)}})})}},{"./util.js":6}],4:[function(e,t){var n=function(e,t,n,r,i,o){function a(i){var a=document.createElement("canvas"),u=a.getContext("2d");return i=function(){var e=document.createElement("canvas").getContext("2d"),t=window.devicePixelRatio||1,n=e.webkitBackingStorePixelRatio||e.mozBackingStorePixelRatio||e.msBackingStorePixelRatio||e.oBackingStorePixelRatio||e.backingStorePixelRatio||1;return t/n}(),a.width=e*i,a.height=t*i,a.style.width=e+"px",a.style.height=t+"px",u.setTransform(i,0,0,i,0,0),u.font="bold "+n+"px Arial",r&&(a.id=r),o&&(a.style.background="rgb("+o+")"),a}e*=n,t*=n;var u=a();return i?i.parentNode.insertBefore(u,i.nextSibling):document.body.appendChild(u),u};t.exports={createCanvasElement:n}},{}],5:[function(e,t){function n(e,t,n){var r=n.cellSize||10;this.width=e,this.height=t,this.cellSize=r,this.trails=n.trails,this.background=n.background,this.canvas=a.createCanvasElement(e,t,r,n.id,n.insertAfter,this.background),this.grid=[],this.nextFrame=!1,this.hasChanged=!1}var r=e("./util"),i=e("./creature.js"),o=e("./display.js"),a=e("./dom.js");n.prototype.makeGrid=function(e){for(var t=[],n=typeof e,r=0,o=this.width;o>r;r++){t.push([]);for(var a=0,u=this.height;u>a;a++)t[r].push(i.make("function"===n?e(r,a):"object"===n&&e.length?(e[a]||[])[r]:"string"===n?e:void 0))}return t},n.prototype.makeGridWithDistribution=function(e){for(var t=[],n=0,o=this.width;o>n;n++){t.push([]);for(var a=0,u=this.height;u>a;a++)t[n].push(i.make(r.pickRandomWeighted(e)))}return t},n.prototype.step=function(e){function t(e){if(e){var t=r.assign(new e.constructor,e),n=t&&t.isDead();return n&&!p.hasChanged&&(p.hasChanged=!0),t.age++,n?!1:t}return!1}function n(e){return r.map(e,t)}function i(e){return{coords:e,creature:h[e.x][e.y]}}function o(e){var t=e.creature;t?(t.failureFn(),t.boundEnergy()):(e.wait(),e.boundEnergy())}function a(e,t,n){if(e){var a=r.map(r.getNeighborCoords(t,n,g-1,d-1,e.actionRadius),i),u=e.process(a,t,n);if("object"==typeof u){var c=f[u.x],s=u.creature,l=u.y;c[l]||(c[l]=[]),c[l].push({x:t,y:n,creature:s}),!p.hasChanged&&s.observed&&(p.hasChanged=!0)}else u&&!p.hasChanged&&(p.hasChanged=!0),o(e)}}function u(e,t){r.each(e,function(e,n){a(e,t,n)})}function c(e,t,n){if(e){var i=e.splice(r.random(e.length-1),1)[0],a=i.creature;a.successFn()||(l[i.x][i.y]=!1),a.boundEnergy(),l[t][n]=a,r.each(e,o)}}function s(e,t){r.each(e,function(e,n){c(e,t,n)})}var l,f,p=this,g=this.width,d=this.height,h=this.grid;for("number"!=typeof e&&(e=1);e--;)if(this.hasChanged=!1,h=l?r.clone(l):this.grid,l=r.map(h,n),f=this.makeGrid(),r.each(l,u),r.each(f,s),!this.hasChanged)return!1;return l},n.prototype.draw=function(){o(this.canvas,this.grid,this.cellSize,this.trails,this.background)},n.prototype.animate=function(e,t){function n(){var o=i.step();return o&&(i.grid=o,i.draw(),++r!==e)?i.nextFrame=requestAnimationFrame(n):(i.nextFrame=!1,void(t&&t()))}if("function"==typeof e&&(t=e,e=null),!this.nextFrame){var r=0,i=this;i.nextFrame=requestAnimationFrame(n)}},n.prototype.stop=function(){cancelAnimationFrame(this.nextFrame),this.nextFrame=!1},t.exports=n},{"./creature.js":2,"./display.js":3,"./dom.js":4,"./util":6}],6:[function(e,t){e("../bower_components/seedrandom/seedrandom.js")("terra :)",{global:!0});var n=e("../lodash_custom/lodash.custom.min.js")._;n.getNeighborCoords=function(e,t,n,r,i){var o,a,u,c,s=[];("number"!=typeof i||1>i)&&(i=1),o=Math.max(0,e-i),u=Math.max(0,t-i),a=Math.min(e+i,n),c=Math.min(t+i,r);for(var l=o;a>=l;l++)for(var f=u;c>=f;f++)(l!==e||f!==t)&&s.push({x:l,y:f});return s},n.pickRandomWeighted=function(e){var t,r,i=0,o=n.random(100,!0);for(r=0,_len=e.length;_len>r;r++)if(t=e[r],i+=t[1],i>o)return t[0];return!1},t.exports=n},{"../bower_components/seedrandom/seedrandom.js":7,"../lodash_custom/lodash.custom.min.js":9}],7:[function(e,t){!function(e,t,n,r,i,o,a,u,c){function s(e){var t,n=e.length,i=this,o=0,a=i.i=i.j=0,u=i.S=[];for(n||(e=[n++]);r>o;)u[o]=o++;for(o=0;r>o;o++)u[o]=u[a=m&a+e[o%n]+(t=u[o])],u[a]=t;(i.g=function(e){for(var t,n=0,o=i.i,a=i.j,u=i.S;e--;)t=u[o=m&o+1],n=n*r+u[m&(u[o]=u[a=m&a+t])+(u[a]=t)];return i.i=o,i.j=a,n})(r)}function l(e,t){var n,r=[],i=typeof e;if(t&&"object"==i)for(n in e)try{r.push(l(e[n],t-1))}catch(o){}return r.length?r:"string"==i?e:e+"\x00"}function f(e,t){for(var n,r=e+"",i=0;ie;)e=(e+n)*r,t*=r,n=v.g(1);for(;e>=y;)e/=2,t/=2,n>>>=1;return(e+n)/t},m,"global"in o?o.global:this==n)};f(n[c](),t),a&&a.exports?a.exports=v:u&&u.amd&&u(function(){return v})}(this,[],Math,256,6,52,"object"==typeof t&&t,"function"==typeof define&&define,"random")},{}],8:[function(e){var t=e("../../app/creature.js"),n=e("../../app/util.js");t.registerCreature({type:"plant",color:[0,120,0],size:10,initialEnergy:5,maxEnergy:20,wait:function(){this.energy+=1},move:!1,reproduceLv:.65}),t.registerCreature({type:"brute",color:[0,255,255],maxEnergy:50,initialEnergy:10,size:20}),t.registerCreature({type:"bully",color:[241,196,15],initialEnergy:20,reproduceLv:.6,sustainability:3}),t.registerCA({type:"GoL",colorFn:function(){return this.alive?this.color+",1":"0,0,0,0"},process:function(e){var t=n.filter(e,function(e){return e.creature.alive}).length;return this.alive=3===t||2===t&&this.alive,!0}},function(){this.alive=Math.random()<.5}),t.registerCA({type:"cyclic",colors:["255,0,0,1","255,96,0,1","255,191,0,1","223,255,0,1","128,255,0,1","32,255,0,1","0,255,64,1","0,255,159,1","0,255,255,1","0,159,255,1","0,64,255,1","32,0,255,1","127,0,255,1","223,0,255,1","255,0,191,1","255,0,96,1"],colorFn:function(){return this.colors[this.state]},process:function(e){var t=(this.state+1)%16,r=n.some(e,function(e){return e.creature.state===t});return r&&(this.state=t),!0}},function(){this.state=Math.floor(16*Math.random())}),t.registerCreature({type:"secondCreature",color:[154,109,235],initialEnergy:100,sustainability:6,reproduceLv:1,moveLv:0}),t.registerCreature({type:"simplePlant",color:[166,226,46],colorFn:function(){return this.color+",1"},size:10,initialEnergy:5,maxEnergy:20,wait:function(){this.energy+=1},move:!1,reproduceLv:.65}),t.registerCA({type:"elementary",alive:!1,ruleset:[1,0,0,1,0,0,1,0].reverse(),colorFn:function(){return this.alive?this.color+",1":"0,0,0,0"},process:function(e,t,r){if(this.age===r){var i=n.map(n.filter(e,function(e){return e.coords.y===r-1}),function(e){return e.creature.alive?1:0});i=parseInt(i.join(""),2),this.alive=isNaN(i)?!t:this.ruleset[i]}return!0}})},{"../../app/creature.js":2,"../../app/util.js":6}],9:[function(e,t,n){(function(e){(function(){function r(e){return"function"!=typeof e.toString&&"string"==typeof(e+"")}function i(e){e.length=0,F.length<_&&F.push(e)}function o(e,t){var n;t||(t=0),"undefined"==typeof n&&(n=e?e.length:0);var r=-1;n=n-t||0;for(var i=Array(0>n?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||wt.nonEnumArgs)&&(r+="}"),r+=n.c+";return E",e("d,j,k,m,o,p,q,s,v,A,B,y,I,J,L",t+r+"}")(l,R,tt,ct,N,y,xt,b,U.f,nt,Y,bt,J,rt,it)}function h(e){return"function"==typeof e&&ot.test(e)}function y(e){return e&&"object"==typeof e&&"number"==typeof e.length&&it.call(e)==z||!1}function m(e){return"function"==typeof e}function v(e){return!(!e||!Y[typeof e])}function b(e){return"string"==typeof e||e&&"object"==typeof e&&it.call(e)==J||!1}function w(e,t,n){var r=[];if(t=a.createCallback(t,n,3),xt(e)){n=-1;for(var i=e.length;++narguments.length;if(t=a.createCallback(t,r,4),xt(e)){var o=-1,u=e.length;for(i&&(n=e[++o]);++o3&&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]"}),At=d(X,Ct,{j:!1}),Pt=d(X,Ct);m(/x/)&&(m=function(e){return"function"==typeof e&&"[object Function]"==it.call(e)}),a.assign=Ot,a.bind=S,a.createCallback=function(e,t,n){var r=typeof e;if(null==e||"function"==r)return l(e,t,n);if("object"!=r)return P(e);var i=jt(e),o=i[0],a=e[o];return 1!=i.length||a!==a||v(a)?function(t){for(var n=i.length,r=!1;n--&&(r=p(t[i[n]],e[i[n]],null,!0)););return r}:function(e){return e=e[o],a===e&&(0!==a||1/a==1/e)}},a.filter=w,a.forEach=x,a.forIn=At,a.forOwn=Pt,a.keys=jt,a.map=E,a.property=P,a.collect=E,a.each=x,a.extend=Ot,a.select=w,a.clone=function(e,t,n,r){return"boolean"!=typeof t&&null!=t&&(r=n,n=t,t=!1),c(e,t,"function"==typeof n&&l(n,r,1))},a.identity=O,a.isArguments=y,a.isArray=xt,a.isFunction=m,a.isObject=v,a.isString=b,a.noop=A,a.random=function(e,t,n){var r=null==e,i=null==t;return null==n&&("boolean"==typeof e&&i?(n=e,e=1):i||"boolean"!=typeof t||(n=t,i=!0)),r&&i&&(t=1),e=+e||0,i?(t=e,e=0):t=+t||0,n||e%1||t%1?(n=mt(),yt(e+n*(t-e+parseFloat("1e-"+((n+"").length-1))),t)):e+at(mt()*(t-e+1))},a.reduce=j,a.some=C,a.any=C,a.foldl=j,a.inject=j,a.VERSION="2.4.1",V&&Z&&(V._=a)}).call(this)}).call(this,"undefined"!=typeof global?global:"undefined"!=typeof self?self:"undefined"!=typeof window?window:{})},{}]},{},[1]); \ No newline at end of file