mirror of
https://github.com/rileyjshaw/terra.git
synced 2024-11-21 04:54:23 +00:00
v0.0.2-alpha
This commit is contained in:
parent
e261f14635
commit
8dfdc93eeb
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
.sass-cache
|
||||||
|
demo
|
||||||
|
bower_components
|
||||||
|
node_modules
|
||||||
|
index.html
|
||||||
|
main.css
|
||||||
|
terra.demo.min.js
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 rileyjshaw
|
||||||
|
|
||||||
|
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.
|
170
app/creature.js
Normal file
170
app/creature.js
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
var _ = require('./util.js');
|
||||||
|
|
||||||
|
// abstract factory that adds a superclass of baseCreature
|
||||||
|
var creatureFactory = (function () {
|
||||||
|
function baseCreature() {
|
||||||
|
this.age = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseCreature.prototype.initialEnergy = 50;
|
||||||
|
baseCreature.prototype.maxEnergy = 100;
|
||||||
|
baseCreature.prototype.metabolism = 0.7;
|
||||||
|
baseCreature.prototype.size = 50;
|
||||||
|
baseCreature.prototype.speed = 1;
|
||||||
|
baseCreature.prototype.vision = 1;
|
||||||
|
baseCreature.prototype.sustainability = 2;
|
||||||
|
// used as percentages of maxEnergy
|
||||||
|
baseCreature.prototype.reproduceLv = 0.70;
|
||||||
|
baseCreature.prototype.moveLv = 0.20;
|
||||||
|
|
||||||
|
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 = creatureFactory.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.metabolism;
|
||||||
|
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 (neighbors) {
|
||||||
|
this.energy -= 5;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
baseCreature.prototype.queue = function (neighbors) {
|
||||||
|
var step = {};
|
||||||
|
var maxEnergy = this.maxEnergy;
|
||||||
|
|
||||||
|
if (this.energy > this.maxEnergy * this.reproduceLv && this.reproduce) {
|
||||||
|
step = this.reproduce(neighbors);
|
||||||
|
} else if (this.energy > 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
|
||||||
|
};
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Storage for our creature types
|
||||||
|
var types = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
make: function (type, options) {
|
||||||
|
var Creature = types[type];
|
||||||
|
return (Creature ? new Creature(options) : false);
|
||||||
|
},
|
||||||
|
|
||||||
|
register: function (options, init) {
|
||||||
|
// required attributes
|
||||||
|
var type = options.type;
|
||||||
|
var color = options.color;
|
||||||
|
// only register classes that fulfill the creature contract
|
||||||
|
if (typeof type === 'string' &&
|
||||||
|
typeof types[type] === 'undefined' &&
|
||||||
|
typeof color === 'object' &&
|
||||||
|
color.length === 3) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
module.exports = creatureFactory;
|
25
app/display.js
Normal file
25
app/display.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
var _ = require('./util.js');
|
||||||
|
|
||||||
|
module.exports = function(canvas, grid, cellSize) {
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
ctx.font = 'bold ' + cellSize + 'px Arial';
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
41
app/dom.js
Normal file
41
app/dom.js
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// Creates an HD canvas element on page and
|
||||||
|
// returns a reference to the element
|
||||||
|
var createCanvasElement = function (width, height, id, insertAfter) {
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
// Creates a dummy canvas to test device's pixel ratio
|
||||||
|
ratio = (function () {
|
||||||
|
var context = document.createElement('canvas').getContext('2d');
|
||||||
|
var dpr = window.devicePixelRatio || 1;
|
||||||
|
var bsr = context.webkitBackingStorePixelRatio ||
|
||||||
|
context.mozBackingStorePixelRatio ||
|
||||||
|
context.msBackingStorePixelRatio ||
|
||||||
|
context.oBackingStorePixelRatio ||
|
||||||
|
context.backingStorePixelRatio || 1;
|
||||||
|
return dpr / bsr;
|
||||||
|
})();
|
||||||
|
|
||||||
|
canvas.width = width * ratio;
|
||||||
|
canvas.height = height * ratio;
|
||||||
|
canvas.style.width = width + 'px';
|
||||||
|
canvas.style.height = height + 'px';
|
||||||
|
canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);
|
||||||
|
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
|
||||||
|
};
|
7
app/main.js
Normal file
7
app/main.js
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
var Terrarium = require('./terrarium.js');
|
||||||
|
var creatureFactory = require('./creature.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Terrarium: Terrarium,
|
||||||
|
creatureFactory: creatureFactory
|
||||||
|
};
|
153
app/terrarium.js
Normal file
153
app/terrarium.js
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
var _ = require('./util');
|
||||||
|
var creatureFactory = require('./creature.js');
|
||||||
|
var display = require('./display.js');
|
||||||
|
var dom = require('./dom.js');
|
||||||
|
|
||||||
|
function Terrarium(width, height, id, cellSize, insertAfter) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.cellSize = cellSize || 10;
|
||||||
|
this.grid = [];
|
||||||
|
this.canvas = dom.createCanvasElement(width * cellSize, height * cellSize, id, insertAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Terrarium.prototype.populate = function (creatures, grid) {
|
||||||
|
function pickCreature(accum, creature) {
|
||||||
|
var percentage = accum + creature[1];
|
||||||
|
if (!current && percentage > rand) {
|
||||||
|
current = creatureFactory.make(creature[0]);
|
||||||
|
}
|
||||||
|
return percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current, rand = 0;
|
||||||
|
if (!grid) grid = this.grid;
|
||||||
|
|
||||||
|
for (var x = this.width; x--;) {
|
||||||
|
grid[x] = [];
|
||||||
|
// populate the array with creatures if provided,
|
||||||
|
// otherwise leave it sparse
|
||||||
|
if (creatures) {
|
||||||
|
for (var y = this.height; y--;) {
|
||||||
|
current = false;
|
||||||
|
rand = _.random(99, true);
|
||||||
|
_.reduce(creatures, pickCreature, 0);
|
||||||
|
grid[x].push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Terrarium.prototype.step = function(steps) {
|
||||||
|
function copyAndRemoveInner (origCreature) {
|
||||||
|
if (origCreature) {
|
||||||
|
var copy = _.assign(new (origCreature.constructor)(), origCreature);
|
||||||
|
return copy.isDead() ? false : copy;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyAndRemove (origCols) {
|
||||||
|
return _.map(origCols, copyAndRemoveInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.vision),
|
||||||
|
zipCoordsWithNeighbors
|
||||||
|
);
|
||||||
|
var result = creature.queue(neighbors);
|
||||||
|
if (result) {
|
||||||
|
var eigenColumn = eigenGrid[result.x];
|
||||||
|
if (!eigenColumn[result.y]) eigenColumn[result.y] = [];
|
||||||
|
|
||||||
|
eigenColumn[result.y].push({
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
creature: result.creature
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
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() === false) {
|
||||||
|
newGrid[winner.x][winner.y] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 gridWidth = this.width;
|
||||||
|
var gridHeight = this.height;
|
||||||
|
var oldGrid = this.grid, newGrid, eigenGrid;
|
||||||
|
|
||||||
|
if (typeof steps !== 'number') steps = 1;
|
||||||
|
|
||||||
|
while (steps--) {
|
||||||
|
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.populate(false, eigenGrid);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGrid;
|
||||||
|
};
|
||||||
|
|
||||||
|
Terrarium.prototype.draw = function() {
|
||||||
|
display(this.canvas, this.grid, this.cellSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = Terrarium;
|
38
app/util.js
Normal file
38
app/util.js
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CommonJS exports
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
module.exports = _;
|
38
bower.json
Normal file
38
bower.json
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "terra",
|
||||||
|
"version": "0.0.2-alpha",
|
||||||
|
"homepage": "https://github.com/rileyjshaw/terra",
|
||||||
|
"authors": [
|
||||||
|
"rileyjshaw <i@rileyjshaw.com>"
|
||||||
|
],
|
||||||
|
"description": "A JavaScript framework for simple biological simulations and cellular automata.",
|
||||||
|
"main": "dist/terra.min.js",
|
||||||
|
"moduleType": [
|
||||||
|
"node"
|
||||||
|
],
|
||||||
|
"keywords": [
|
||||||
|
"terrarium",
|
||||||
|
"sumulator",
|
||||||
|
"cellular",
|
||||||
|
"automata",
|
||||||
|
"biology"
|
||||||
|
],
|
||||||
|
"license": "MIT",
|
||||||
|
"private": true,
|
||||||
|
"ignore": [
|
||||||
|
"**/.*",
|
||||||
|
"app",
|
||||||
|
"bower_components",
|
||||||
|
"demo",
|
||||||
|
"lodash_custom",
|
||||||
|
"node_modules",
|
||||||
|
"tests",
|
||||||
|
"index.html",
|
||||||
|
"main.css",
|
||||||
|
"terra.demo.min.js",
|
||||||
|
"gulpfile.js"
|
||||||
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"seedrandom": "~2.3.6"
|
||||||
|
}
|
||||||
|
}
|
7319
dist/all.js
vendored
7319
dist/all.js
vendored
File diff suppressed because it is too large
Load Diff
2
dist/all.min.js
vendored
2
dist/all.min.js
vendored
File diff suppressed because one or more lines are too long
1
dist/all.min.js.map
vendored
1
dist/all.min.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/main.css
vendored
1
dist/main.css
vendored
@ -1 +0,0 @@
|
|||||||
body{text-align:center}p{display:inline-block}span{float:left;font-family:monospace;white-space:pre}
|
|
849
dist/terra.js
vendored
Executable file
849
dist/terra.js
vendored
Executable file
@ -0,0 +1,849 @@
|
|||||||
|
!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<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
||||||
|
var Terrarium = require('./terrarium.js');
|
||||||
|
var creatureFactory = require('./creature.js');
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Terrarium: Terrarium,
|
||||||
|
creatureFactory: creatureFactory
|
||||||
|
};
|
||||||
|
|
||||||
|
},{"./creature.js":2,"./terrarium.js":5}],2:[function(require,module,exports){
|
||||||
|
var _ = require('./util.js');
|
||||||
|
|
||||||
|
// abstract factory that adds a superclass of baseCreature
|
||||||
|
var creatureFactory = (function () {
|
||||||
|
function baseCreature() {
|
||||||
|
this.age = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseCreature.prototype.initialEnergy = 50;
|
||||||
|
baseCreature.prototype.maxEnergy = 100;
|
||||||
|
baseCreature.prototype.metabolism = 0.7;
|
||||||
|
baseCreature.prototype.size = 50;
|
||||||
|
baseCreature.prototype.speed = 1;
|
||||||
|
baseCreature.prototype.vision = 1;
|
||||||
|
baseCreature.prototype.sustainability = 2;
|
||||||
|
// used as percentages of maxEnergy
|
||||||
|
baseCreature.prototype.reproduceLv = 0.70;
|
||||||
|
baseCreature.prototype.moveLv = 0.20;
|
||||||
|
|
||||||
|
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 = creatureFactory.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.metabolism;
|
||||||
|
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 (neighbors) {
|
||||||
|
this.energy -= 5;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
baseCreature.prototype.queue = function (neighbors) {
|
||||||
|
var step = {};
|
||||||
|
var maxEnergy = this.maxEnergy;
|
||||||
|
|
||||||
|
if (this.energy > this.maxEnergy * this.reproduceLv && this.reproduce) {
|
||||||
|
step = this.reproduce(neighbors);
|
||||||
|
} else if (this.energy > 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
|
||||||
|
};
|
||||||
|
} else return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Storage for our creature types
|
||||||
|
var types = {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
make: function (type, options) {
|
||||||
|
var Creature = types[type];
|
||||||
|
return (Creature ? new Creature(options) : false);
|
||||||
|
},
|
||||||
|
|
||||||
|
register: function (options, init) {
|
||||||
|
// required attributes
|
||||||
|
var type = options.type;
|
||||||
|
var color = options.color;
|
||||||
|
// only register classes that fulfill the creature contract
|
||||||
|
if (typeof type === 'string' &&
|
||||||
|
typeof types[type] === 'undefined' &&
|
||||||
|
typeof color === 'object' &&
|
||||||
|
color.length === 3) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
module.exports = creatureFactory;
|
||||||
|
|
||||||
|
},{"./util.js":6}],3:[function(require,module,exports){
|
||||||
|
var _ = require('./util.js');
|
||||||
|
|
||||||
|
module.exports = function(canvas, grid, cellSize) {
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
ctx.font = 'bold ' + cellSize + 'px Arial';
|
||||||
|
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, id, insertAfter) {
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
// Creates a dummy canvas to test device's pixel ratio
|
||||||
|
ratio = (function () {
|
||||||
|
var context = document.createElement('canvas').getContext('2d');
|
||||||
|
var dpr = window.devicePixelRatio || 1;
|
||||||
|
var bsr = context.webkitBackingStorePixelRatio ||
|
||||||
|
context.mozBackingStorePixelRatio ||
|
||||||
|
context.msBackingStorePixelRatio ||
|
||||||
|
context.oBackingStorePixelRatio ||
|
||||||
|
context.backingStorePixelRatio || 1;
|
||||||
|
return dpr / bsr;
|
||||||
|
})();
|
||||||
|
|
||||||
|
canvas.width = width * ratio;
|
||||||
|
canvas.height = height * ratio;
|
||||||
|
canvas.style.width = width + 'px';
|
||||||
|
canvas.style.height = height + 'px';
|
||||||
|
canvas.getContext('2d').setTransform(ratio, 0, 0, ratio, 0, 0);
|
||||||
|
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 creatureFactory = require('./creature.js');
|
||||||
|
var display = require('./display.js');
|
||||||
|
var dom = require('./dom.js');
|
||||||
|
|
||||||
|
function Terrarium(width, height, id, cellSize, insertAfter) {
|
||||||
|
this.width = width;
|
||||||
|
this.height = height;
|
||||||
|
this.cellSize = cellSize || 10;
|
||||||
|
this.grid = [];
|
||||||
|
this.canvas = dom.createCanvasElement(width * cellSize, height * cellSize, id, insertAfter);
|
||||||
|
}
|
||||||
|
|
||||||
|
Terrarium.prototype.populate = function (creatures, grid) {
|
||||||
|
function pickCreature(accum, creature) {
|
||||||
|
var percentage = accum + creature[1];
|
||||||
|
if (!current && percentage > rand) {
|
||||||
|
current = creatureFactory.make(creature[0]);
|
||||||
|
}
|
||||||
|
return percentage;
|
||||||
|
}
|
||||||
|
|
||||||
|
var current, rand = 0;
|
||||||
|
if (!grid) grid = this.grid;
|
||||||
|
|
||||||
|
for (var x = this.width; x--;) {
|
||||||
|
grid[x] = [];
|
||||||
|
// populate the array with creatures if provided,
|
||||||
|
// otherwise leave it sparse
|
||||||
|
if (creatures) {
|
||||||
|
for (var y = this.height; y--;) {
|
||||||
|
current = false;
|
||||||
|
rand = _.random(99, true);
|
||||||
|
_.reduce(creatures, pickCreature, 0);
|
||||||
|
grid[x].push(current);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Terrarium.prototype.step = function(steps) {
|
||||||
|
function copyAndRemoveInner (origCreature) {
|
||||||
|
if (origCreature) {
|
||||||
|
var copy = _.assign(new (origCreature.constructor)(), origCreature);
|
||||||
|
return copy.isDead() ? false : copy;
|
||||||
|
} else return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyAndRemove (origCols) {
|
||||||
|
return _.map(origCols, copyAndRemoveInner);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.vision),
|
||||||
|
zipCoordsWithNeighbors
|
||||||
|
);
|
||||||
|
var result = creature.queue(neighbors);
|
||||||
|
if (result) {
|
||||||
|
var eigenColumn = eigenGrid[result.x];
|
||||||
|
if (!eigenColumn[result.y]) eigenColumn[result.y] = [];
|
||||||
|
|
||||||
|
eigenColumn[result.y].push({
|
||||||
|
x: x,
|
||||||
|
y: y,
|
||||||
|
creature: result.creature
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
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() === false) {
|
||||||
|
newGrid[winner.x][winner.y] = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 gridWidth = this.width;
|
||||||
|
var gridHeight = this.height;
|
||||||
|
var oldGrid = this.grid, newGrid, eigenGrid;
|
||||||
|
|
||||||
|
if (typeof steps !== 'number') steps = 1;
|
||||||
|
|
||||||
|
while (steps--) {
|
||||||
|
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.populate(false, eigenGrid);
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return newGrid;
|
||||||
|
};
|
||||||
|
|
||||||
|
Terrarium.prototype.draw = function() {
|
||||||
|
display(this.canvas, this.grid, this.cellSize);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<br>
|
||||||
|
Author: David Bau<br>
|
||||||
|
Date: 2014 May 14
|
||||||
|
|
||||||
|
Can be used as a plain script, a node.js module or an AMD module.
|
||||||
|
|
||||||
|
Script tag usage
|
||||||
|
----------------
|
||||||
|
|
||||||
|
<script src=//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.6/seedrandom.min.js>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
// 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
|
||||||
|
--------------------------------
|
||||||
|
|
||||||
|
<script src=//cdnjs.cloudflare.com/ajax/libs/seedrandom/2.3.6/seedrandom.min.js>
|
||||||
|
</script>
|
||||||
|
<!-- Seeds using urandom bits from a server. -->
|
||||||
|
<script src=//jsonlib.appspot.com/urandom?callback=Math.seedrandom">
|
||||||
|
</script>
|
||||||
|
|
||||||
|
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.length<C&&S.push(n)}function e(n,t){var e;t||(t=0),typeof e=="undefined"&&(e=n?n.length:0);var r=-1;e=e-t||0;for(var o=Array(0>e?0:e);++r<e;)o[r]=n[t+r];return o}function r(){}function o(n){function t(){if(o){var n=e(o);at.apply(n,arguments)}if(this instanceof t){var c=a(r.prototype),n=r.apply(c,n||arguments);return h(n)?n:c}return r.apply(u,n||arguments)}var r=n[0],o=n[2],u=n[4];return dt(t,n),t
|
||||||
|
}function u(r,o,a,c,i){if(a){var l=a(r);if(typeof l!="undefined")return l}if(!h(r))return r;var f=tt.call(r);if(!G[f]||!bt.nodeClass&&n(r))return r;var p=ht[f];switch(f){case R:case L:return new p(+r);case T:case M:return new p(r);case K:return l=p(r.source,D.exec(r)),l.lastIndex=r.lastIndex,l}if(f=mt(r),o){var s=!c;c||(c=S.pop()||[]),i||(i=S.pop()||[]);for(var g=c.length;g--;)if(c[g]==r)return i[g];l=f?p(r.length):{}}else l=f?e(r):xt({},r);return f&&(ut.call(r,"index")&&(l.index=r.index),ut.call(r,"input")&&(l.input=r.input)),o?(c.push(r),i.push(l),(f?_t:St)(r,function(n,t){l[t]=u(n,o,a,c,i)
|
||||||
|
}),s&&(t(c),t(i)),l):l}function a(n){return h(n)?ft(n):{}}function c(n,t,e){if(typeof n!="function")return _;if(typeof t=="undefined"||!("prototype"in n))return n;var r=n.__bindData__;if(typeof r=="undefined"&&(bt.funcNames&&(r=!n.name),r=r||!bt.funcDecomp,!r)){var o=ot.call(n);bt.funcNames||(r=!P.test(o)),r||(r=F.test(o),dt(n,r))}if(false===r||true!==r&&1&r[1])return n;switch(e){case 1:return function(e){return n.call(t,e)};case 2:return function(e,r){return n.call(t,e,r)};case 3:return function(e,r,o){return n.call(t,e,r,o)
|
||||||
|
};case 4:return function(e,r,o,u){return n.call(t,e,r,o,u)}}return w(n,t)}function i(n){function t(){var n=p?l:this;if(u){var b=e(u);at.apply(b,arguments)}return(c||g)&&(b||(b=e(arguments)),c&&at.apply(b,c),g&&b.length<f)?(o|=16,i([r,y?o:-4&o,b,null,l,f])):(b||(b=arguments),s&&(r=n[v]),this instanceof t?(n=a(r.prototype),b=r.apply(n,b),h(b)?b:n):r.apply(n,b))}var r=n[0],o=n[1],u=n[2],c=n[3],l=n[4],f=n[5],p=1&o,s=2&o,g=4&o,y=8&o,v=r;return dt(t,n),t}function l(e,r,o,u,a,c){if(o){var i=o(e,r);if(typeof i!="undefined")return!!i
|
||||||
|
}if(e===r)return 0!==e||1/e==1/r;if(e===e&&!(e&&H[typeof e]||r&&H[typeof r]))return false;if(null==e||null==r)return e===r;var f=tt.call(e),p=tt.call(r);if(f==B&&(f=z),p==B&&(p=z),f!=p)return false;switch(f){case R:case L:return+e==+r;case T:return e!=+e?r!=+r:0==e?1/e==1/r:e==+r;case K:case M:return e==r+""}if(p=f==N,!p){var s=ut.call(e,"__wrapped__"),h=ut.call(r,"__wrapped__");if(s||h)return l(s?e.__wrapped__:e,h?r.__wrapped__:r,o,u,a,c);if(f!=z||!bt.nodeClass&&(n(e)||n(r)))return false;if(f=!bt.argsObject&&g(e)?Object:e.constructor,s=!bt.argsObject&&g(r)?Object:r.constructor,f!=s&&!(y(f)&&f instanceof f&&y(s)&&s instanceof s)&&"constructor"in e&&"constructor"in r)return false
|
||||||
|
}for(f=!a,a||(a=S.pop()||[]),c||(c=S.pop()||[]),s=a.length;s--;)if(a[s]==e)return c[s]==r;var v=0,i=true;if(a.push(e),c.push(r),p){if(s=e.length,v=r.length,(i=v==s)||u)for(;v--;)if(p=s,h=r[v],u)for(;p--&&!(i=l(e[p],h,o,u,a,c)););else if(!(i=l(e[v],h,o,u,a,c)))break}else Ot(r,function(n,t,r){return ut.call(r,t)?(v++,i=ut.call(e,t)&&l(e[t],n,o,u,a,c)):void 0}),i&&!u&&Ot(e,function(n,t,e){return ut.call(e,t)?i=-1<--v:void 0});return a.pop(),c.pop(),f&&(t(a),t(c)),i}function f(n,t,r,u,a,c){var l=1&t,p=4&t,s=16&t,g=32&t;
|
||||||
|
if(!(2&t||y(n)))throw new TypeError;s&&!r.length&&(t&=-17,s=r=false),g&&!u.length&&(t&=-33,g=u=false);var h=n&&n.__bindData__;return h&&true!==h?(h=e(h),h[2]&&(h[2]=e(h[2])),h[3]&&(h[3]=e(h[3])),!l||1&h[1]||(h[4]=a),!l&&1&h[1]&&(t|=8),!p||4&h[1]||(h[5]=c),s&&at.apply(h[2]||(h[2]=[]),r),g&&it.apply(h[3]||(h[3]=[]),u),h[1]|=t,f.apply(null,h)):(1==t||17===t?o:i)([n,t,r,u,a,c])}function p(){q.h=I,q.b=q.c=q.g=q.i="",q.e="t",q.j=true;for(var n,t=0;n=arguments[t];t++)for(var e in n)q[e]=n[e];t=q.a,q.d=/^[^,]+/.exec(t)[0],n=Function,t="return function("+t+"){",e=q;
|
||||||
|
var r="var n,t="+e.d+",E="+e.e+";if(!t)return E;"+e.i+";";e.b?(r+="var u=t.length;n=-1;if("+e.b+"){",bt.unindexedChars&&(r+="if(s(t)){t=t.split('')}"),r+="while(++n<u){"+e.g+";}}else{"):bt.nonEnumArgs&&(r+="var u=t.length;n=-1;if(u&&p(t)){while(++n<u){n+='';"+e.g+";}}else{"),bt.enumPrototypes&&(r+="var G=typeof t=='function';"),bt.enumErrorProps&&(r+="var F=t===k||t instanceof Error;");var o=[];if(bt.enumPrototypes&&o.push('!(G&&n=="prototype")'),bt.enumErrorProps&&o.push('!(F&&(n=="message"||n=="name"))'),e.j&&e.f)r+="var C=-1,D=B[typeof t]&&v(t),u=D?D.length:0;while(++C<u){n=D[C];",o.length&&(r+="if("+o.join("&&")+"){"),r+=e.g+";",o.length&&(r+="}"),r+="}";
|
||||||
|
else if(r+="for(n in t){",e.j&&o.push("m.call(t, n)"),o.length&&(r+="if("+o.join("&&")+"){"),r+=e.g+";",o.length&&(r+="}"),r+="}",bt.nonEnumShadows){for(r+="if(t!==A){var i=t.constructor,r=t===(i&&i.prototype),f=t===J?I:t===k?j:L.call(t),x=y[f];",k=0;7>k;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;++e<u;){var a=n[e];t(a,e,n)&&o.push(a)}}else _t(n,function(n,e,r){t(n,e,r)&&o.push(n)});return o}function d(n,t,e){if(t&&typeof e=="undefined"&&mt(n)){e=-1;
|
||||||
|
for(var r=n.length;++e<r&&false!==t(n[e],e,n););}else _t(n,t,e);return n}function m(n,t,e){var o=-1,u=n?n.length:0,a=Array(typeof u=="number"?u:0);if(t=r.createCallback(t,e,3),mt(n))for(;++o<u;)a[o]=t(n[o],o,n);else _t(n,function(n,e,r){a[++o]=t(n,e,r)});return a}function j(n,t,e,o){var u=3>arguments.length;if(t=r.createCallback(t,o,4),mt(n)){var a=-1,c=n.length;for(u&&(e=n[++a]);++a<c;)e=t(e,n[a],a,n)}else _t(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)});return e}function E(n,t,e){var o;if(t=r.createCallback(t,e,3),mt(n)){e=-1;
|
||||||
|
for(var u=n.length;++e<u&&!(o=t(n[e],e,n)););}else _t(n,function(n,e,r){return!(o=t(n,e,r))});return!!o}function w(n,t){return 2<arguments.length?f(n,17,e(arguments,2),null,t):f(n,1,null,null,t)}function _(n){return n}function x(){}function O(n){return function(t){return t[n]}}var S=[],A={},C=40,D=/\w*$/,P=/^\s*function[ \n\r\t]+\w/,F=/\bthis\b/,I="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),B="[object Arguments]",N="[object Array]",R="[object Boolean]",L="[object Date]",$="[object Error]",T="[object Number]",z="[object Object]",K="[object RegExp]",M="[object String]",G={"[object Function]":false};
|
||||||
|
G[B]=G[N]=G[R]=G[L]=G[T]=G[z]=G[K]=G[M]=true;var J={configurable:false,enumerable:false,value:null,writable:false},q={a:"",b:null,c:"",d:"",e:"",v:null,g:"",h:null,support:null,i:"",j:false},H={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},V=H[typeof window]&&window||this,W=H[typeof exports]&&exports&&!exports.nodeType&&exports,Q=H[typeof module]&&module&&!module.nodeType&&module,U=H[typeof global]&&global;!U||U.global!==U&&U.window!==U||(V=U);var X=[],Y=Error.prototype,Z=Object.prototype,nt=String.prototype,tt=Z.toString,et=RegExp("^"+(tt+"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),rt=Math.floor,ot=Function.prototype.toString,ut=Z.hasOwnProperty,at=X.push,ct=Z.propertyIsEnumerable,it=X.unshift,lt=function(){try{var n={},t=s(t=Object.defineProperty)&&t,e=t(n,n,n)&&t
|
||||||
|
}catch(r){}return e}(),ft=s(ft=Object.create)&&ft,pt=s(pt=Array.isArray)&&pt,st=s(st=Object.keys)&&st,gt=Math.min,yt=Math.random,ht={};ht[N]=Array,ht[R]=Boolean,ht[L]=Date,ht["[object Function]"]=Function,ht[z]=Object,ht[T]=Number,ht[K]=RegExp,ht[M]=String;var vt={};vt[N]=vt[L]=vt[T]={constructor:true,toLocaleString:true,toString:true,valueOf:true},vt[R]=vt[M]={constructor:true,toString:true,valueOf:true},vt[$]=vt["[object Function]"]=vt[K]={constructor:true,toString:true},vt[z]={constructor:true},function(){for(var n=I.length;n--;){var t,e=I[n];
|
||||||
|
for(t in vt)ut.call(vt,t)&&!ut.call(vt[t],e)&&(vt[t][e]=false)}}();var bt=r.support={};!function(){function n(){this.x=1}var t={0:1,length:1},e=[];n.prototype={valueOf:1,y:1};for(var r in new n)e.push(r);for(r in arguments);bt.argsClass=tt.call(arguments)==B,bt.argsObject=arguments.constructor==Object&&!(arguments instanceof Array),bt.enumErrorProps=ct.call(Y,"message")||ct.call(Y,"name"),bt.enumPrototypes=ct.call(n,"prototype"),bt.funcDecomp=!s(V.k)&&F.test(function(){return this}),bt.funcNames=typeof Function.name=="string",bt.nonEnumArgs=0!=r,bt.nonEnumShadows=!/valueOf/.test(e),bt.spliceObjects=(X.splice.call(t,0,1),!t[0]),bt.unindexedChars="xx"!="x"[0]+Object("x")[0];
|
||||||
|
try{bt.nodeClass=!(tt.call(document)==z&&!({toString:0}+""))}catch(o){bt.nodeClass=true}}(1),ft||(a=function(){function n(){}return function(t){if(h(t)){n.prototype=t;var e=new n;n.prototype=null}return e||V.Object()}}());var dt=lt?function(n,t){J.value=t,lt(n,"__bindData__",J)}:x;bt.argsClass||(g=function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ut.call(n,"callee")&&!ct.call(n,"callee")||false});var mt=pt||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&tt.call(n)==N||false
|
||||||
|
},jt=p({a:"z",e:"[]",i:"if(!(B[typeof z]))return E",g:"E.push(n)"}),Et=st?function(n){return h(n)?bt.enumPrototypes&&typeof n=="function"||bt.nonEnumArgs&&n.length&&g(n)?jt(n):st(n):[]}:jt,U={a:"g,e,K",i:"e=e&&typeof K=='undefined'?e:d(e,K,3)",b:"typeof u=='number'",v:Et,g:"if(e(t[n],n,g)===false)return E"},pt={a:"z,H,l",i:"var a=arguments,b=0,c=typeof l=='number'?2:a.length;while(++b<c){t=a[b];if(t&&B[typeof t]){",v:Et,g:"if(typeof E[n]=='undefined')E[n]=t[n]",c:"}}"},wt={i:"if(!B[typeof t])return E;"+U.i,b:false},_t=p(U),xt=p(pt,{i:pt.i.replace(";",";if(c>3&&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)
|
||||||
|
});
|
1
dist/terra.min.js
vendored
Executable file
1
dist/terra.min.js
vendored
Executable file
File diff suppressed because one or more lines are too long
95
gulpfile.js
95
gulpfile.js
@ -1,56 +1,75 @@
|
|||||||
var
|
var gulp = require('gulp');
|
||||||
gulp = require('gulp'),
|
var $ = require('gulp-load-plugins')({lazy: false});
|
||||||
jshint = require('gulp-jshint'),
|
var source = require('vinyl-source-stream');
|
||||||
mocha = require('gulp-mocha'),
|
var browserify = require('browserify');
|
||||||
stripDebug = require('gulp-strip-debug'),
|
|
||||||
concat = require('gulp-concat'),
|
var argv = require('minimist')(process.argv.slice(2));
|
||||||
uglify = require('gulp-uglify'),
|
|
||||||
rename = require('gulp-rename');
|
|
||||||
sass = require('gulp-ruby-sass'),
|
|
||||||
autoprefixer = require('gulp-autoprefixer'),
|
|
||||||
minifyCSS = require('gulp-minify-css');
|
|
||||||
|
|
||||||
var paths = {
|
var paths = {
|
||||||
src: {
|
app: {
|
||||||
firstScripts: [ 'node_modules/lodash/lodash.js', 'scripts/terra.js', 'scripts/terra.util.js' ],
|
entry: './app/main.js',
|
||||||
scripts: 'scripts/*.js',
|
all: './app/**/*.js',
|
||||||
tests: 'tests/*.js',
|
ext: ['./bower_components/**/*.js', './lodash_custom/**/*.js']
|
||||||
stylesheets: 'stylesheets/*.sass'
|
},
|
||||||
}
|
demo: {
|
||||||
|
entry: ['./demo/scripts/main.js'],
|
||||||
|
scripts: ['./demo/scripts/**/*.js'],
|
||||||
|
stylesheets: ['./demo/stylesheets/**/*.css', './demo/stylesheets/**/*.sass'],
|
||||||
|
extras: ['./*.{png,ico,txt,xml}']
|
||||||
|
},
|
||||||
|
dist: {
|
||||||
|
scripts: './dist',
|
||||||
|
demo: './'
|
||||||
|
},
|
||||||
|
tests: './tests'
|
||||||
};
|
};
|
||||||
|
|
||||||
gulp.task('lint', function() {
|
gulp.task('lint', function() {
|
||||||
return gulp.src(paths.src.scripts)
|
return gulp.src(paths.app.all)
|
||||||
.pipe(jshint())
|
.pipe($.jshint())
|
||||||
.pipe(jshint.reporter('default'));
|
.pipe($.jshint.reporter('default'));
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('test', function() {
|
gulp.task('test', function() {
|
||||||
return gulp.src(paths.src.tests)
|
return gulp.src(paths.tests)
|
||||||
.pipe( mocha( { reporter: 'spec' } ) )
|
.pipe( $.mocha( { reporter: 'spec' } ) )
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('scripts', function() {
|
gulp.task('scripts', ['lint'], function() {
|
||||||
return gulp.src( [ paths.src.firstScripts[0], paths.src.firstScripts[1], paths.src.firstScripts[2], paths.src.scripts ] )
|
return browserify(paths.app.entry, {
|
||||||
.pipe(stripDebug())
|
debug: argv.debug,
|
||||||
.pipe(concat('all.js'))
|
standalone: 'terra'
|
||||||
.pipe(gulp.dest('dist'))
|
})
|
||||||
.pipe(rename('all.min.js'))
|
.bundle()
|
||||||
.pipe(uglify( { outSourceMap: true } ))
|
.pipe(source('terra.js'))
|
||||||
.pipe(gulp.dest('dist'));
|
.pipe(gulp.dest(paths.dist.scripts))
|
||||||
|
.pipe($.rename('terra.min.js'))
|
||||||
|
.pipe($.streamify( $.uglify() ))
|
||||||
|
.pipe(gulp.dest(paths.dist.scripts))
|
||||||
|
});
|
||||||
|
|
||||||
|
gulp.task('demo', function() {
|
||||||
|
return browserify(paths.demo.entry, {
|
||||||
|
debug: argv.debug
|
||||||
|
})
|
||||||
|
.bundle()
|
||||||
|
.pipe(source('terra.demo.min.js'))
|
||||||
|
.pipe($.streamify( $.uglify() ))
|
||||||
|
.pipe(gulp.dest(paths.dist.demo))
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('sass', function () {
|
gulp.task('sass', function () {
|
||||||
return gulp.src(paths.src.stylesheets)
|
return gulp.src(paths.demo.stylesheets)
|
||||||
.pipe(sass())
|
.pipe($.rubySass())
|
||||||
.pipe(autoprefixer())
|
.pipe($.autoprefixer())
|
||||||
.pipe(minifyCSS())
|
.pipe($.minifyCss())
|
||||||
.pipe(gulp.dest('dist'))
|
.pipe(gulp.dest(paths.dist.demo))
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task('watch', function() {
|
gulp.task('watch', function() {
|
||||||
gulp.watch(paths.src.scripts, ['lint', 'scripts']);
|
gulp.watch([paths.app.all, paths.app.ext], ['lint', 'scripts']);
|
||||||
gulp.watch(paths.src.stylesheets, ['sass']);
|
gulp.watch(paths.demo.scripts, ['demo']);
|
||||||
|
gulp.watch(paths.demo.stylesheets, ['sass']);
|
||||||
});
|
});
|
||||||
|
|
||||||
gulp.task( 'default', [ 'lint', 'scripts', 'sass', 'watch' ] );
|
gulp.task( 'default', [ 'lint', 'scripts', 'demo', 'sass', 'watch' ] );
|
||||||
|
102
index.html
102
index.html
@ -2,11 +2,105 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>Awesome Terrarium!</title>
|
<title>terra.js</title>
|
||||||
<link rel="stylesheet" href="dist/main.css">
|
<link rel="stylesheet" href="main.css">
|
||||||
|
<link href='http://fonts.googleapis.com/css?family=Open+Sans:400,700' rel='stylesheet' type='text/css'>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<p id='terra'>Life</p>
|
<div class="fullPage splash">
|
||||||
<script src="dist/all.min.js"></script>
|
<div class="vcent"></div>
|
||||||
|
<header id="header">
|
||||||
|
<h1>terra.js <span>alpha</span></h1>
|
||||||
|
<p>A JavaScript framework for simple biological simulations and cellular automata.</p>
|
||||||
|
<a data-scroll href="#main" class="downArrow">⬇</a>
|
||||||
|
</header>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a data-scroll href="#usage">Usage</a></li>
|
||||||
|
<li><a data-scroll href="#examples">Examples</a></li>
|
||||||
|
<li><a data-scroll href="#options">Options</a></li>
|
||||||
|
<li><a data-scroll href="#methods">Methods</a></li>
|
||||||
|
<li><a data-scroll href="#events">Events</a></li>
|
||||||
|
<li><a href="https://github.com/rileyjshaw/terra">GitHub</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<div id="main" class="main">
|
||||||
|
<p>terra is a <strong>super customizable</strong> framework for creating and analyzing biological simulations. It's open-source and licenced under MIT.</p>
|
||||||
|
<p>terra is still in Alpha version, which means...</p>
|
||||||
|
<h2 id="usage">Usage</h2>
|
||||||
|
<p>To get started, we'll create a simple creature using the creatureFactory. Each creature requires a type and a color.</p>
|
||||||
|
<pre>creatureFactory.register({
|
||||||
|
type: 'firstCreature',
|
||||||
|
color: [220, 85, 79]
|
||||||
|
});</pre>
|
||||||
|
<p>This creature is valid, but it's just inheriting the default behaviours. To make a more interesting creature, let's override some of the default <a data-scroll href="#options">options</a> and <a data-scroll href="#methods">methods</a>.</p>
|
||||||
|
<pre>creatureFactory.register({
|
||||||
|
type: 'secondCreature',
|
||||||
|
color: [41, 128, 185],
|
||||||
|
metabolism: 1,
|
||||||
|
sustainability: 3,
|
||||||
|
reproduceLv: 0.60,
|
||||||
|
etc...
|
||||||
|
});</pre>
|
||||||
|
<p>We've just created a creature that will ____.</p>
|
||||||
|
<p>To run a simulation, we'll need to create an environment. Let's make a 25x25 grid and populate 40% of the space with our first creature and 20% with our second.</p>
|
||||||
|
<pre>var ex1 = new Terrarium(25, 25, 'ex1');
|
||||||
|
ex1.populate([['firstCreature', 40], ['secondCreature', 20]]);</pre>
|
||||||
|
|
||||||
|
|
||||||
|
<h2 id="examples">Examples</h2>
|
||||||
|
<h3 id="gol">Conway's Game of Life <a class="question" target="_blank" href="http://en.wikipedia.org/wiki/Conway's_Game_of_Life">?</a></h3>
|
||||||
|
<p>terra can also run cellular automata. </p>
|
||||||
|
<pre><code>
|
||||||
|
|
||||||
|
</code></pre>
|
||||||
|
|
||||||
|
<h2 id="options">Options</h2>
|
||||||
|
<p>The following options can be passed in an object to creatureFactory.register( ) as the first argument.</p>
|
||||||
|
|
||||||
|
<h3>Required</h3>
|
||||||
|
<ul>
|
||||||
|
<li>(string) type: </li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h3>Optional</h3>
|
||||||
|
<ul>
|
||||||
|
<li>(array) color: </li>
|
||||||
|
<li>(int) initialEnergy: </li>
|
||||||
|
<li>(int) maxEnergy: </li>
|
||||||
|
<li>(int) metabolism: </li>
|
||||||
|
<li>(int) size: </li>
|
||||||
|
<li>(int) speed: </li>
|
||||||
|
<li>(int) vision: </li>
|
||||||
|
<li>(int) sustainability: 2</li>
|
||||||
|
<li>(float) reproduceLv: 0.70</li>
|
||||||
|
<li>(float) moveLv: 0.20</li>
|
||||||
|
<li>(char) character: </li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="methods">Methods</h2>
|
||||||
|
<ul>
|
||||||
|
<li>queue</li>
|
||||||
|
<li>reproduce</li>
|
||||||
|
<li>move</li>
|
||||||
|
<li>wait</li>
|
||||||
|
<li>isDead</li>
|
||||||
|
<li>boundEnergy</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<h2 id="events">Events</h2>
|
||||||
|
<ul>
|
||||||
|
<li>finished</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer>Created with ❤ by <a href="http://rileyjshaw.com">rileyjshaw</a>. Inspired by <a href="http://eloquentjavascript.net/">Marijn Haverbeke</a> and <a href="https://www.wolframscience.com/">Stephen Wolfram</a>.</footer>
|
||||||
|
<script src="bower_components/smooth-scroll.js/dist/js/bind-polyfill.min.js"></script>
|
||||||
|
<script src="bower_components/smooth-scroll.js/dist/js/smooth-scroll.min.js"></script>
|
||||||
|
<script src="terra.demo.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
2080
lodash_custom/lodash.custom.js
Normal file
2080
lodash_custom/lodash.custom.js
Normal file
File diff suppressed because it is too large
Load Diff
24
lodash_custom/lodash.custom.min.js
vendored
Normal file
24
lodash_custom/lodash.custom.min.js
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @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.length<C&&S.push(n)}function e(n,t){var e;t||(t=0),typeof e=="undefined"&&(e=n?n.length:0);var r=-1;e=e-t||0;for(var o=Array(0>e?0:e);++r<e;)o[r]=n[t+r];return o}function r(){}function o(n){function t(){if(o){var n=e(o);at.apply(n,arguments)}if(this instanceof t){var c=a(r.prototype),n=r.apply(c,n||arguments);return h(n)?n:c}return r.apply(u,n||arguments)}var r=n[0],o=n[2],u=n[4];return dt(t,n),t
|
||||||
|
}function u(r,o,a,c,i){if(a){var l=a(r);if(typeof l!="undefined")return l}if(!h(r))return r;var f=tt.call(r);if(!G[f]||!bt.nodeClass&&n(r))return r;var p=ht[f];switch(f){case R:case L:return new p(+r);case T:case M:return new p(r);case K:return l=p(r.source,D.exec(r)),l.lastIndex=r.lastIndex,l}if(f=mt(r),o){var s=!c;c||(c=S.pop()||[]),i||(i=S.pop()||[]);for(var g=c.length;g--;)if(c[g]==r)return i[g];l=f?p(r.length):{}}else l=f?e(r):xt({},r);return f&&(ut.call(r,"index")&&(l.index=r.index),ut.call(r,"input")&&(l.input=r.input)),o?(c.push(r),i.push(l),(f?_t:St)(r,function(n,t){l[t]=u(n,o,a,c,i)
|
||||||
|
}),s&&(t(c),t(i)),l):l}function a(n){return h(n)?ft(n):{}}function c(n,t,e){if(typeof n!="function")return _;if(typeof t=="undefined"||!("prototype"in n))return n;var r=n.__bindData__;if(typeof r=="undefined"&&(bt.funcNames&&(r=!n.name),r=r||!bt.funcDecomp,!r)){var o=ot.call(n);bt.funcNames||(r=!P.test(o)),r||(r=F.test(o),dt(n,r))}if(false===r||true!==r&&1&r[1])return n;switch(e){case 1:return function(e){return n.call(t,e)};case 2:return function(e,r){return n.call(t,e,r)};case 3:return function(e,r,o){return n.call(t,e,r,o)
|
||||||
|
};case 4:return function(e,r,o,u){return n.call(t,e,r,o,u)}}return w(n,t)}function i(n){function t(){var n=p?l:this;if(u){var b=e(u);at.apply(b,arguments)}return(c||g)&&(b||(b=e(arguments)),c&&at.apply(b,c),g&&b.length<f)?(o|=16,i([r,y?o:-4&o,b,null,l,f])):(b||(b=arguments),s&&(r=n[v]),this instanceof t?(n=a(r.prototype),b=r.apply(n,b),h(b)?b:n):r.apply(n,b))}var r=n[0],o=n[1],u=n[2],c=n[3],l=n[4],f=n[5],p=1&o,s=2&o,g=4&o,y=8&o,v=r;return dt(t,n),t}function l(e,r,o,u,a,c){if(o){var i=o(e,r);if(typeof i!="undefined")return!!i
|
||||||
|
}if(e===r)return 0!==e||1/e==1/r;if(e===e&&!(e&&H[typeof e]||r&&H[typeof r]))return false;if(null==e||null==r)return e===r;var f=tt.call(e),p=tt.call(r);if(f==B&&(f=z),p==B&&(p=z),f!=p)return false;switch(f){case R:case L:return+e==+r;case T:return e!=+e?r!=+r:0==e?1/e==1/r:e==+r;case K:case M:return e==r+""}if(p=f==N,!p){var s=ut.call(e,"__wrapped__"),h=ut.call(r,"__wrapped__");if(s||h)return l(s?e.__wrapped__:e,h?r.__wrapped__:r,o,u,a,c);if(f!=z||!bt.nodeClass&&(n(e)||n(r)))return false;if(f=!bt.argsObject&&g(e)?Object:e.constructor,s=!bt.argsObject&&g(r)?Object:r.constructor,f!=s&&!(y(f)&&f instanceof f&&y(s)&&s instanceof s)&&"constructor"in e&&"constructor"in r)return false
|
||||||
|
}for(f=!a,a||(a=S.pop()||[]),c||(c=S.pop()||[]),s=a.length;s--;)if(a[s]==e)return c[s]==r;var v=0,i=true;if(a.push(e),c.push(r),p){if(s=e.length,v=r.length,(i=v==s)||u)for(;v--;)if(p=s,h=r[v],u)for(;p--&&!(i=l(e[p],h,o,u,a,c)););else if(!(i=l(e[v],h,o,u,a,c)))break}else Ot(r,function(n,t,r){return ut.call(r,t)?(v++,i=ut.call(e,t)&&l(e[t],n,o,u,a,c)):void 0}),i&&!u&&Ot(e,function(n,t,e){return ut.call(e,t)?i=-1<--v:void 0});return a.pop(),c.pop(),f&&(t(a),t(c)),i}function f(n,t,r,u,a,c){var l=1&t,p=4&t,s=16&t,g=32&t;
|
||||||
|
if(!(2&t||y(n)))throw new TypeError;s&&!r.length&&(t&=-17,s=r=false),g&&!u.length&&(t&=-33,g=u=false);var h=n&&n.__bindData__;return h&&true!==h?(h=e(h),h[2]&&(h[2]=e(h[2])),h[3]&&(h[3]=e(h[3])),!l||1&h[1]||(h[4]=a),!l&&1&h[1]&&(t|=8),!p||4&h[1]||(h[5]=c),s&&at.apply(h[2]||(h[2]=[]),r),g&&it.apply(h[3]||(h[3]=[]),u),h[1]|=t,f.apply(null,h)):(1==t||17===t?o:i)([n,t,r,u,a,c])}function p(){q.h=I,q.b=q.c=q.g=q.i="",q.e="t",q.j=true;for(var n,t=0;n=arguments[t];t++)for(var e in n)q[e]=n[e];t=q.a,q.d=/^[^,]+/.exec(t)[0],n=Function,t="return function("+t+"){",e=q;
|
||||||
|
var r="var n,t="+e.d+",E="+e.e+";if(!t)return E;"+e.i+";";e.b?(r+="var u=t.length;n=-1;if("+e.b+"){",bt.unindexedChars&&(r+="if(s(t)){t=t.split('')}"),r+="while(++n<u){"+e.g+";}}else{"):bt.nonEnumArgs&&(r+="var u=t.length;n=-1;if(u&&p(t)){while(++n<u){n+='';"+e.g+";}}else{"),bt.enumPrototypes&&(r+="var G=typeof t=='function';"),bt.enumErrorProps&&(r+="var F=t===k||t instanceof Error;");var o=[];if(bt.enumPrototypes&&o.push('!(G&&n=="prototype")'),bt.enumErrorProps&&o.push('!(F&&(n=="message"||n=="name"))'),e.j&&e.f)r+="var C=-1,D=B[typeof t]&&v(t),u=D?D.length:0;while(++C<u){n=D[C];",o.length&&(r+="if("+o.join("&&")+"){"),r+=e.g+";",o.length&&(r+="}"),r+="}";
|
||||||
|
else if(r+="for(n in t){",e.j&&o.push("m.call(t, n)"),o.length&&(r+="if("+o.join("&&")+"){"),r+=e.g+";",o.length&&(r+="}"),r+="}",bt.nonEnumShadows){for(r+="if(t!==A){var i=t.constructor,r=t===(i&&i.prototype),f=t===J?I:t===k?j:L.call(t),x=y[f];",k=0;7>k;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;++e<u;){var a=n[e];t(a,e,n)&&o.push(a)}}else _t(n,function(n,e,r){t(n,e,r)&&o.push(n)});return o}function d(n,t,e){if(t&&typeof e=="undefined"&&mt(n)){e=-1;
|
||||||
|
for(var r=n.length;++e<r&&false!==t(n[e],e,n););}else _t(n,t,e);return n}function m(n,t,e){var o=-1,u=n?n.length:0,a=Array(typeof u=="number"?u:0);if(t=r.createCallback(t,e,3),mt(n))for(;++o<u;)a[o]=t(n[o],o,n);else _t(n,function(n,e,r){a[++o]=t(n,e,r)});return a}function j(n,t,e,o){var u=3>arguments.length;if(t=r.createCallback(t,o,4),mt(n)){var a=-1,c=n.length;for(u&&(e=n[++a]);++a<c;)e=t(e,n[a],a,n)}else _t(n,function(n,r,o){e=u?(u=false,n):t(e,n,r,o)});return e}function E(n,t,e){var o;if(t=r.createCallback(t,e,3),mt(n)){e=-1;
|
||||||
|
for(var u=n.length;++e<u&&!(o=t(n[e],e,n)););}else _t(n,function(n,e,r){return!(o=t(n,e,r))});return!!o}function w(n,t){return 2<arguments.length?f(n,17,e(arguments,2),null,t):f(n,1,null,null,t)}function _(n){return n}function x(){}function O(n){return function(t){return t[n]}}var S=[],A={},C=40,D=/\w*$/,P=/^\s*function[ \n\r\t]+\w/,F=/\bthis\b/,I="constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf".split(" "),B="[object Arguments]",N="[object Array]",R="[object Boolean]",L="[object Date]",$="[object Error]",T="[object Number]",z="[object Object]",K="[object RegExp]",M="[object String]",G={"[object Function]":false};
|
||||||
|
G[B]=G[N]=G[R]=G[L]=G[T]=G[z]=G[K]=G[M]=true;var J={configurable:false,enumerable:false,value:null,writable:false},q={a:"",b:null,c:"",d:"",e:"",v:null,g:"",h:null,support:null,i:"",j:false},H={"boolean":false,"function":true,object:true,number:false,string:false,undefined:false},V=H[typeof window]&&window||this,W=H[typeof exports]&&exports&&!exports.nodeType&&exports,Q=H[typeof module]&&module&&!module.nodeType&&module,U=H[typeof global]&&global;!U||U.global!==U&&U.window!==U||(V=U);var X=[],Y=Error.prototype,Z=Object.prototype,nt=String.prototype,tt=Z.toString,et=RegExp("^"+(tt+"").replace(/[.*+?^${}()|[\]\\]/g,"\\$&").replace(/toString| for [^\]]+/g,".*?")+"$"),rt=Math.floor,ot=Function.prototype.toString,ut=Z.hasOwnProperty,at=X.push,ct=Z.propertyIsEnumerable,it=X.unshift,lt=function(){try{var n={},t=s(t=Object.defineProperty)&&t,e=t(n,n,n)&&t
|
||||||
|
}catch(r){}return e}(),ft=s(ft=Object.create)&&ft,pt=s(pt=Array.isArray)&&pt,st=s(st=Object.keys)&&st,gt=Math.min,yt=Math.random,ht={};ht[N]=Array,ht[R]=Boolean,ht[L]=Date,ht["[object Function]"]=Function,ht[z]=Object,ht[T]=Number,ht[K]=RegExp,ht[M]=String;var vt={};vt[N]=vt[L]=vt[T]={constructor:true,toLocaleString:true,toString:true,valueOf:true},vt[R]=vt[M]={constructor:true,toString:true,valueOf:true},vt[$]=vt["[object Function]"]=vt[K]={constructor:true,toString:true},vt[z]={constructor:true},function(){for(var n=I.length;n--;){var t,e=I[n];
|
||||||
|
for(t in vt)ut.call(vt,t)&&!ut.call(vt[t],e)&&(vt[t][e]=false)}}();var bt=r.support={};!function(){function n(){this.x=1}var t={0:1,length:1},e=[];n.prototype={valueOf:1,y:1};for(var r in new n)e.push(r);for(r in arguments);bt.argsClass=tt.call(arguments)==B,bt.argsObject=arguments.constructor==Object&&!(arguments instanceof Array),bt.enumErrorProps=ct.call(Y,"message")||ct.call(Y,"name"),bt.enumPrototypes=ct.call(n,"prototype"),bt.funcDecomp=!s(V.k)&&F.test(function(){return this}),bt.funcNames=typeof Function.name=="string",bt.nonEnumArgs=0!=r,bt.nonEnumShadows=!/valueOf/.test(e),bt.spliceObjects=(X.splice.call(t,0,1),!t[0]),bt.unindexedChars="xx"!="x"[0]+Object("x")[0];
|
||||||
|
try{bt.nodeClass=!(tt.call(document)==z&&!({toString:0}+""))}catch(o){bt.nodeClass=true}}(1),ft||(a=function(){function n(){}return function(t){if(h(t)){n.prototype=t;var e=new n;n.prototype=null}return e||V.Object()}}());var dt=lt?function(n,t){J.value=t,lt(n,"__bindData__",J)}:x;bt.argsClass||(g=function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&ut.call(n,"callee")&&!ct.call(n,"callee")||false});var mt=pt||function(n){return n&&typeof n=="object"&&typeof n.length=="number"&&tt.call(n)==N||false
|
||||||
|
},jt=p({a:"z",e:"[]",i:"if(!(B[typeof z]))return E",g:"E.push(n)"}),Et=st?function(n){return h(n)?bt.enumPrototypes&&typeof n=="function"||bt.nonEnumArgs&&n.length&&g(n)?jt(n):st(n):[]}:jt,U={a:"g,e,K",i:"e=e&&typeof K=='undefined'?e:d(e,K,3)",b:"typeof u=='number'",v:Et,g:"if(e(t[n],n,g)===false)return E"},pt={a:"z,H,l",i:"var a=arguments,b=0,c=typeof l=='number'?2:a.length;while(++b<c){t=a[b];if(t&&B[typeof t]){",v:Et,g:"if(typeof E[n]=='undefined')E[n]=t[n]",c:"}}"},wt={i:"if(!B[typeof t])return E;"+U.i,b:false},_t=p(U),xt=p(pt,{i:pt.i.replace(";",";if(c>3&&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);
|
35
package.json
35
package.json
@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "terra",
|
"name": "terra",
|
||||||
"version": "0.0.2",
|
"version": "0.0.2-alpha",
|
||||||
"description": "A JavaScript framework for generating, analysing, and displaying simple biological interactions in an abstract, digital terrarium.",
|
"description": "A JavaScript framework for simple biological simulations and cellular automata.",
|
||||||
"main": "scripts/terra.js",
|
"main": "dist/terra.min.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
@ -12,19 +12,24 @@
|
|||||||
"cellular",
|
"cellular",
|
||||||
"automata"
|
"automata"
|
||||||
],
|
],
|
||||||
"author": "rileyjshaw",
|
"author": "rileyjshaw <i@rileyjshaw.com>",
|
||||||
"license": "BSD-2-Clause",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"gulp": "~3.5.2",
|
"gulp-rename": "~1.2.0",
|
||||||
"gulp-rename": "~1.1.0",
|
"gulp-concat": "~2.2.0",
|
||||||
"gulp-autoprefixer": "0.0.6",
|
"gulp-ruby-sass": "~0.5.0",
|
||||||
"gulp-uglify": "~0.2.1",
|
"gulp-uglify": "~0.3.1",
|
||||||
"gulp-minify-css": "~0.3.0",
|
"gulp-strip-debug": "~0.3.0",
|
||||||
"gulp-strip-debug": "~0.1.1",
|
"gulp": "~3.8.5",
|
||||||
"gulp-jshint": "~1.4.2",
|
"gulp-autoprefixer": "0.0.8",
|
||||||
"gulp-concat": "~2.1.7",
|
"gulp-jshint": "~1.6.4",
|
||||||
"gulp-ruby-sass": "~0.3.1",
|
"gulp-minify-css": "~0.3.6",
|
||||||
"gulp-mocha": "~0.4.1"
|
"gulp-mocha": "~0.4.1",
|
||||||
|
"minimist": "~0.2.0",
|
||||||
|
"gulp-load-plugins": "~0.5.3",
|
||||||
|
"vinyl-source-stream": "~0.1.1",
|
||||||
|
"browserify": "~5.10.0",
|
||||||
|
"gulp-streamify": "0.0.5"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "~2.4.1"
|
"lodash": "~2.4.1"
|
||||||
|
@ -1,37 +0,0 @@
|
|||||||
// consider changing name, adding ability to pause, reset, etc.
|
|
||||||
/*
|
|
||||||
terra.create = function ( selector, map, fps, steps ) {
|
|
||||||
|
|
||||||
// Imports...
|
|
||||||
Terrarium = terra.util.Terrarium;
|
|
||||||
|
|
||||||
if ( typeof selector !== 'string' ) {
|
|
||||||
throw new Error( 'You need to pass an element selector (string) as the first argument of terra.create (received "' + selector + '")');
|
|
||||||
}
|
|
||||||
|
|
||||||
map = map || terra.maps.mSmall( [ '*', 'C', ' ', '#' ] );
|
|
||||||
fps = fps || 60;
|
|
||||||
steps = steps || 1;
|
|
||||||
|
|
||||||
var startTick = function ( fn ) {
|
|
||||||
var tick = function () {
|
|
||||||
setTimeout( function () {
|
|
||||||
fn();
|
|
||||||
requestAnimationFrame( tick );
|
|
||||||
}, 1000 / fps );
|
|
||||||
};
|
|
||||||
|
|
||||||
requestAnimationFrame( tick );
|
|
||||||
};
|
|
||||||
|
|
||||||
var level = new Terrarium( map, selector );
|
|
||||||
|
|
||||||
startTick( function () {
|
|
||||||
for ( var i = steps; i; i-- ) {
|
|
||||||
level.step();
|
|
||||||
}
|
|
||||||
level.toDom();
|
|
||||||
});
|
|
||||||
|
|
||||||
};
|
|
||||||
*/
|
|
@ -1,13 +0,0 @@
|
|||||||
;( function ( exports ) {
|
|
||||||
|
|
||||||
var creature = function ( config, my ) {
|
|
||||||
var
|
|
||||||
color = [ 230, 180, 25 ],
|
|
||||||
instance = {},
|
|
||||||
character = config.character,
|
|
||||||
energy = config.energy || 20,
|
|
||||||
speed = config.speed || 1;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
})( terra.creatures = terra.creatures || {} );
|
|
@ -1,10 +0,0 @@
|
|||||||
;( function () {
|
|
||||||
if ( typeof terra !== 'undefined' &&
|
|
||||||
terra !== document.getElementsByName( 'terra' ) &&
|
|
||||||
terra !== document.getElementById( 'terra' )
|
|
||||||
) {
|
|
||||||
throw 'Variable "terra" already exists, that\'s a problem.';
|
|
||||||
} else {
|
|
||||||
terra = {}; // global
|
|
||||||
}
|
|
||||||
} )();
|
|
@ -1,51 +0,0 @@
|
|||||||
/*;( function ( exports ) {
|
|
||||||
|
|
||||||
// Imports...
|
|
||||||
randomElement = terra.util.randomElement;
|
|
||||||
|
|
||||||
var mGameOfLife =
|
|
||||||
[ '############################',
|
|
||||||
'# L #',
|
|
||||||
'# L #',
|
|
||||||
'# L #',
|
|
||||||
'# #',
|
|
||||||
'# L #',
|
|
||||||
'# L #',
|
|
||||||
'# LLL #',
|
|
||||||
'# L #',
|
|
||||||
'# L #',
|
|
||||||
'# #',
|
|
||||||
'############################' ];
|
|
||||||
|
|
||||||
var mapGen = function ( height, width, characters ) {
|
|
||||||
var level = new Array('');
|
|
||||||
for ( var col = height - 1; col >= 0; col--) {
|
|
||||||
for ( var row = width - 1; row >= 0; row--) {
|
|
||||||
if ( row == width - 1 ) {
|
|
||||||
character = '';
|
|
||||||
level[ col ] = '#';
|
|
||||||
} else if ( !col || !row || col == height - 1 ) {
|
|
||||||
character = '#';
|
|
||||||
} else {
|
|
||||||
character = randomElement( characters );
|
|
||||||
}
|
|
||||||
level[ col ] += character;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return level;
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.mGameOfLife = mGameOfLife;
|
|
||||||
exports.mSmall = function ( characters ) {
|
|
||||||
return mapGen( 24, 48, characters );
|
|
||||||
};
|
|
||||||
exports.mMedium = function ( characters ) {
|
|
||||||
return mapGen( 48, 96, characters );
|
|
||||||
};
|
|
||||||
exports.mBig = function ( characters ) {
|
|
||||||
return mapGen( 56, 144, characters );
|
|
||||||
};
|
|
||||||
exports.mapGen = mapGen;
|
|
||||||
|
|
||||||
})( terra.maps = terra.maps || {} );
|
|
||||||
*/
|
|
@ -1,25 +0,0 @@
|
|||||||
// Utility and helper functions
|
|
||||||
;( function ( exports ) {
|
|
||||||
|
|
||||||
var arrayOfArrays = function ( height, width ) {
|
|
||||||
var instance;
|
|
||||||
|
|
||||||
if ( typeof height !== 'number' || typeof width !== 'number' ) {
|
|
||||||
throw 'Input error: arrayOfArrays() requires two numbers as inputs';
|
|
||||||
}
|
|
||||||
|
|
||||||
instance = new Array( height );
|
|
||||||
while( width-- ) {
|
|
||||||
instance[ width ] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
};
|
|
||||||
|
|
||||||
var grid = new arrayOfArrays(500, 800);
|
|
||||||
|
|
||||||
// grid.map
|
|
||||||
|
|
||||||
exports.grid = grid;
|
|
||||||
|
|
||||||
})( terra.util = terra.util || {} );
|
|
@ -1,10 +0,0 @@
|
|||||||
body
|
|
||||||
text-align: center
|
|
||||||
|
|
||||||
p
|
|
||||||
display: inline-block
|
|
||||||
|
|
||||||
span
|
|
||||||
float: left
|
|
||||||
font-family: monospace
|
|
||||||
white-space: pre
|
|
@ -1,9 +1 @@
|
|||||||
var assert = require("assert")
|
// Big TODO...
|
||||||
describe('Array', function(){
|
|
||||||
describe('#indexOf()', function(){
|
|
||||||
it('should return -1 when the value is not present', function(){
|
|
||||||
assert.equal(-1, [1,2,3].indexOf(5));
|
|
||||||
assert.equal(-1, [1,2,3].indexOf(0));
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
Loading…
Reference in New Issue
Block a user