2014-03-10 03:57:59 +00:00
<!doctype html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
2014-08-17 03:23:48 +00:00
< title > terra.js< / title >
< link rel = "stylesheet" href = "main.css" >
< link href = 'http://fonts.googleapis.com/css?family=Open+Sans:400,700' rel = 'stylesheet' type = 'text/css' >
2014-03-10 03:57:59 +00:00
< / head >
< body >
2014-08-17 03:23:48 +00:00
< div class = "fullPage splash" >
< div class = "vcent" > < / div >
< header id = "header" >
2014-08-26 08:49:35 +00:00
< h1 > terra.js < span > beta< / span > < / h1 >
2014-08-17 03:23:48 +00:00
< p > A JavaScript framework for simple biological simulations and cellular automata.< / p >
2014-08-21 08:07:30 +00:00
< a data-scroll href = "#main" id = "scroller1" class = "downArrow" > ⬇< / a >
2014-08-17 03:23:48 +00:00
< / header >
< / div >
< nav >
< ul >
< li > < a data-scroll href = "#usage" > Usage< / a > < / li >
< li > < a data-scroll href = "#examples" > Examples< / a > < / li >
2014-08-22 23:27:50 +00:00
< li > < a data-scroll href = "#creatures" > Creatures< / a > < / li >
< li > < a data-scroll href = "#terrarium" > Terrarium< / a > < / li >
2014-08-17 03:23:48 +00:00
< 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 >
2014-08-17 06:23:48 +00:00
2014-08-17 03:23:48 +00:00
< h2 id = "usage" > Usage< / h2 >
2014-08-17 06:23:48 +00:00
< h3 > Including terra< / h3 >
< p > Getting started is as easy as including the script!< / p >
< pre > < code class = "language-markup" > < script src="path/to/terra.min.js"> < /script> < / code > < / pre >
2014-08-24 06:40:44 +00:00
< p > terra can also be used as a module with most popular module systems: < a class = "question" target = "_blank" href = "https://github.com/umdjs/umd" > ?< / a > < / p >
< pre > < code class = "language-javascript" > // CommonJS
var terra = require('./terra.min.js');
// ...
// AMD
define(['./terra.min.js'] , function (terra) {
2014-08-25 17:29:42 +00:00
return function () { //... };
2014-08-24 06:40:44 +00:00
});
// ...and if you're not using a module system, it'll be in
window.terra;< / code > < / pre >
< p > If you manage dependencies with < a href = "http://bower.io/" > Bower< / a > , you're in luck!< / p >
< pre > < code class = "language-bash" > bower install terra< / code > < / pre >
2014-08-17 06:23:48 +00:00
< h3 > Creating creatures< / h3 >
2014-08-26 06:23:40 +00:00
< p > Let's create a simple creature using the registerCreature method. Each creature requires a type.< / p >
< pre > < code class = "language-javascript" > terra.registerCreature({
2014-08-17 06:23:48 +00:00
type: 'firstCreature'
});< / code > < / pre >
2014-08-22 23:27:50 +00:00
< p > This creature is valid, but it's pretty boring. To make a more interesting creature, let's override some of the default < a data-scroll href = "#creatures" > attributes and methods< / a > .< / p >
2014-08-26 06:23:40 +00:00
< pre > < code class = "language-javascript" > terra.registerCreature({
2014-08-17 03:23:48 +00:00
type: 'secondCreature',
2014-08-21 08:07:30 +00:00
color: [120, 0, 240],
sustainability: 6,
reproduceLv: 1
2014-08-17 06:23:48 +00:00
});< / code > < / pre >
2014-08-21 08:07:30 +00:00
< p > We've just created a purple creature that only eats if 6 or more plants are around it. These creatures basically seek out an edge or corner and die there.< / p >
2014-08-17 06:23:48 +00:00
2014-08-21 08:07:30 +00:00
< h3 > Creating the environment< / h3 >
< p > To run a simulation, we'll need to create an environment. Let's make a 25x25 grid, populate 10% of the space with our lonely purple creature, and fill the rest with simple plants.< / p >
< pre > < code class = "language-javascript" > var ex1 = new terra.Terrarium(25, 25, 'ex1');
2014-08-25 20:49:28 +00:00
ex1.grid = ex1.makeGridWithDistribution([['secondCreature', 10], ['simplePlant', 90]]);< / code > < / pre >
2014-08-17 06:23:48 +00:00
2014-08-21 08:07:30 +00:00
< h3 > Running a simulation< / h3 >
2014-08-17 06:23:48 +00:00
2014-08-21 08:07:30 +00:00
< p > Terrariums have a few methods that allow you to interact with the simulation. Let's animate it and see how it does for the first 300 steps.< / p >
< pre id = "ex1End" > < code class = "language-javascript" > ex1.animate(300);< / code > < / pre >
2014-08-17 03:23:48 +00:00
2014-08-22 23:27:50 +00:00
< p > That's all there is to it! Though it's possible to generate complex behaviours by simply overriding default values, the real fun comes when you realize that < a data-scroll href = "#customOptions" > creatures are entirely customizable< / a > .< / p >
2014-08-17 03:23:48 +00:00
< h2 id = "examples" > Examples< / h2 >
2014-08-17 06:23:48 +00:00
2014-08-17 03:23:48 +00:00
< 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 >
2014-08-23 23:17:15 +00:00
< pre > < code class = "language-javascript" > var gameOfLife = new terra.Terrarium(25, 25);
2014-08-23 23:16:26 +00:00
2014-08-26 06:23:40 +00:00
terra.registerCA({
2014-08-17 06:23:48 +00:00
type: 'GoL',
colorFn: function () { return this.alive ? this.color + ',1' : '0,0,0,0'; },
2014-08-26 08:33:00 +00:00
process: function (neighbors) {
2014-08-26 06:23:40 +00:00
var surrounding = neighbors.filter(function (spot) {
2014-08-17 06:23:48 +00:00
return spot.creature.alive;
}).length;
2014-08-21 08:07:30 +00:00
this.alive = surrounding === 3 || surrounding === 2 & & this.alive;
2014-08-17 06:23:48 +00:00
return false;
}
}, function () {
2014-08-21 08:07:30 +00:00
this.alive = Math.random() < 0.5;
2014-08-23 23:16:26 +00:00
});
2014-08-25 20:49:28 +00:00
gameOfLife.grid = gameOfLife.makeGrid('GoL');
2014-08-23 23:16:26 +00:00
gameOfLife.animate();< / code > < / pre >
2014-08-17 03:23:48 +00:00
2014-08-21 08:07:30 +00:00
< h3 id = "cyclic" > Cyclic Cellular Automaton < a class = "question" target = "_blank" href = "http://en.wikipedia.org/wiki/Cyclic_cellular_automaton" > ?< / a > < / h3 >
2014-08-24 00:04:02 +00:00
< pre > < code class = "language-javascript" > var cyclic = new terra.Terrarium(100, 100);
2014-08-23 23:16:26 +00:00
2014-08-26 06:23:40 +00:00
terra.registerCA({
2014-08-17 06:23:48 +00:00
type: 'cyclic',
2014-08-21 08:07:30 +00:00
colors: ['255,0,0,1', '255,96,0,1', '255,191,0,1', '223,255,0,1', '128,255,0,1', '32,255,0,1', '0,255,64,1', '0,255,159,1', '0,255,255,1', '0,159,255,1', '0,64,255,1', '32,0,255,1', '127,0,255,1', '223,0,255,1', '255,0,191,1', '255,0,96,1'],
2014-08-17 06:23:48 +00:00
colorFn: function () { return this.colors[this.state];},
2014-08-26 08:33:00 +00:00
process: function (neighbors) {
2014-08-21 08:07:30 +00:00
var next = (this.state + 1) % 16;
2014-08-26 06:23:40 +00:00
var changing = neighbors.some(function (spot) {
2014-08-17 06:23:48 +00:00
return spot.creature.state === next;
});
if (changing) this.state = next;
}
}, function () {
2014-08-21 08:07:30 +00:00
this.state = Math.floor(Math.random() * 16);
2014-08-23 23:16:26 +00:00
});
2014-08-25 20:49:28 +00:00
cyclic.grid = cyclic.makeGrid('cyclic');
2014-08-23 23:16:26 +00:00
cyclic.animate();< / code > < / pre >
2014-08-17 03:23:48 +00:00
2014-08-24 01:36:48 +00:00
< h3 id = "brutesAndBullies" > Brutes and Bullies< / h3 >
2014-08-24 00:04:02 +00:00
< pre > < code class = "language-javascript" > // the demo running at the top of this page
2014-08-25 20:49:28 +00:00
var bbTerrarium = new terra.Terrarium(25, 25);
2014-08-24 00:04:02 +00:00
2014-08-26 06:23:40 +00:00
terra.registerCreature({
2014-08-24 00:04:02 +00:00
type: 'plant',
color: [0, 120, 0],
size: 10,
initialEnergy: 5,
maxEnergy: 20,
wait: function() {
// photosynthesis :)
this.energy += 1;
},
move: false,
reproduceLv: 0.65
});
2014-08-26 06:23:40 +00:00
terra.registerCreature({
2014-08-24 01:36:48 +00:00
type: 'brute',
2014-08-24 00:04:02 +00:00
color: [0, 255, 255],
maxEnergy: 50,
2014-08-24 01:25:47 +00:00
initialEnergy: 10,
size: 20
2014-08-24 00:04:02 +00:00
});
2014-08-26 06:23:40 +00:00
terra.registerCreature({
2014-08-24 00:04:02 +00:00
type: 'bully',
color: [241, 196, 15],
2014-08-24 01:36:48 +00:00
initialEnergy: 20,
2014-08-24 01:25:47 +00:00
reproduceLv: 0.6,
2014-08-24 00:04:02 +00:00
sustainability: 3
});
2014-08-25 20:49:28 +00:00
bbTerrarium.grid = bbTerrarium.makeGridWithDistribution([['plant', 50], ['brute', 5], ['bully', 5]]);
bbTerrarium.animate();< / code > < / pre >
2014-08-24 00:04:02 +00:00
2014-08-22 23:27:50 +00:00
< p > If you come up with a cool example, < a href = "mailto:i@rileyjshaw.com" > let me know< / a > ! I'll add it to this list and credit you.< / p >
2014-08-17 03:23:48 +00:00
2014-08-22 23:27:50 +00:00
< h2 id = "creatures" > Creatures< / h2 >
2014-08-26 06:23:40 +00:00
< p > Creatures are registered with < pre > < code class = "language-javascript" > terra.registerCreature(options, init)< / code > < / pre > or < pre > < code class = "language-javascript" > terra.registerCA(options, init)< / code > < / pre > for cellular automata.< / p >
2014-08-22 23:27:50 +00:00
< p > The following methods and attributes can be passed in an object as the first argument:< / p >
< h3 id = "creatureOptions" > Required< / h3 >
2014-08-21 08:07:30 +00:00
< ul class = "defaults" >
< li >
< h4 > < span class = "token keyword" > string< / span > type< / h4 >
2014-08-25 20:49:28 +00:00
< p > Creature type, to be used later in < a data-scroll href = "#terrariumMethods" class = "token function" > makeGrid( )< / a > or < a data-scroll href = "#terrariumMethods" class = "token function" > makeGridWithDistribution( )< / a > .< / p >
2014-08-21 08:07:30 +00:00
< / li >
2014-08-17 03:23:48 +00:00
< / ul >
< h3 > Optional< / h3 >
2014-08-21 08:07:30 +00:00
< ul class = "defaults" >
2014-08-22 23:27:50 +00:00
< li id = "actionRadius" >
< h4 > < span class = "token keyword" > int< / span > actionRadius< / h4 >
< p > A creature's vision and movement range for each step.< / p >
< ul >
< li > Default: 1< / li >
< / ul >
< / li >
< li >
< h4 > < span class = "token keyword" > char< / span > character< / h4 >
< p > ASCII character used to visually represent a creature.< / p >
< ul >
< li > Default: undefined (fills cell)< / li >
< / ul >
< / li >
2014-08-21 08:07:30 +00:00
< li >
< h4 > < span class = "token keyword" > int [3]< / span > color< / h4 >
< p > RGB components of a creature's display color.< / p >
< ul >
< li > Range: [0, 255]< / li >
< li > Default: random< / li >
< / ul >
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > function< / span > colorFn< / h4 >
< p > How a creature's color is determined at each step.< / p >
< ul >
< li > Returns: < span class = "mono" > < span class = "token keyword" > string< / span > < / span > of comma-separated RGBA components.< / li >
< / ul >
< / li >
< li >
< h4 > < span class = "token keyword" > int< / span > efficiency< / h4 >
< p > Conversion ratio of food to energy. Food energy × efficiency = gained energy.< / p >
< ul >
< li > Default: 0.7< / li >
< / ul >
< / li >
< li id = "initialEnergy" >
2014-08-21 08:07:30 +00:00
< h4 > < span class = "token keyword" > float< / span > initialEnergy< / h4 >
< p > Energy level that a creature has at the start of its life.
< ul >
2014-08-22 23:27:50 +00:00
< li > Range: (0, < a data-scroll href = "#maxEnergy" > maxEnergy< / a > ]< / li >
2014-08-21 08:07:30 +00:00
< li > Default: 50< / li >
< / ul >
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > function< / span > isDead< / h4 >
< p > Determines whether a creature should be removed at the beginning of a step.< / p >
< ul >
< li > Returns: < span class = "mono" > < span class = "token keyword" > boolean< / span > < / span > < / li >
2014-08-26 06:23:40 +00:00
< li > Default: Return true for creatures with energy < = 0. Return false always for cellular automata.< / li >
2014-08-22 23:27:50 +00:00
< / ul >
< / li >
< li id = "maxEnergy" >
2014-08-21 08:07:30 +00:00
< h4 > < span class = "token keyword" > float< / span > maxEnergy< / h4 >
< p > Maximum energy that a creature can have; excess energy is discarded.< / p >
< ul >
< li > Default: 100< / li >
< li > Minimum: 0< / li >
< / ul >
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > function< / span > move< / h4 >
< p > How a creature moves.< / p >
< ul >
< li > Parameters: < span class = "mono" > < span class = "token keyword" > {coords, creature} []< / span > neighbors< / span > < / li >
< li > Default: Look for edible creatures; if none are found, look for open spaces to move to; if none are found, wait.< / li >
< li > Returns: < span class = "mono" > {x, y, creature, successFn} || false< / span > < / li >
< / ul >
2014-08-21 08:07:30 +00:00
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > float< / span > moveLv< / h4 >
2014-08-21 08:07:30 +00:00
< p > < / p >
2014-08-22 23:27:50 +00:00
< ul >
2014-08-26 08:33:00 +00:00
< p > Percentage of a creature's max energy below which it will stop moving (used in the default < a data-scroll href = "#process" > process< / a > method).< / p >
2014-08-24 01:25:47 +00:00
< li > Default: 0< / li >
2014-08-22 23:27:50 +00:00
< li > Range: [0, 1]< / li >
< / ul >
< / li >
2014-08-26 08:33:00 +00:00
< li id = "process" >
< h4 > < span class = "token keyword" > function< / span > process< / h4 >
2014-08-22 23:27:50 +00:00
< p > Main entry point for behavior; called for each creature on each iteration.< / p >
< ul >
< li > Parameters: < span class = "mono" > < span class = "token keyword" > {coords, creature} []< / span > neighbors< / span > < / li >
2014-08-26 06:23:40 +00:00
< li > Default: Creatures reproduce if energy is sufficient, otherwise move if energy is sufficient, otherwise wait. Cellular automata do nothing by default.< / li >
2014-08-22 23:27:50 +00:00
< li > Returns: < span class = "mono" > {x, y, creature} || false< / span > < / li >
< / ul >
2014-08-21 08:07:30 +00:00
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > function< / span > reproduce< / h4 >
< p > How a creature reproduces.< / p >
2014-08-21 08:07:30 +00:00
< ul >
2014-08-22 23:27:50 +00:00
< li > Parameters: < span class = "mono" > < span class = "token keyword" > {coords, creature} []< / span > neighbors< / span > < / li >
< li > Default: Look for neighboring open space; if any exists, randomly place a new creature and lose energy equal to the child's < a data-scroll href = "#initialEnergy" > initialEnergy< / a > .< / li >
< li > Returns: < span class = "mono" > {x, y, creature, successFn, failureFn} || false< / span > < / li >
2014-08-21 08:07:30 +00:00
< / ul >
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > float< / span > reproduceLv< / h4 >
2014-08-26 08:33:00 +00:00
< p > Percentage of a creature's max energy above which it will reproduce (used in the default < a data-scroll href = "#process" > process< / a > method).< / p >
2014-08-22 23:27:50 +00:00
< ul >
< li > Default: 0.7< / li >
< li > Range: [0, 1]< / li >
< / ul >
2014-08-21 08:07:30 +00:00
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > int< / span > size< / h4 >
< p > A creature's size; by default, creatures can only eat creatures smaller than them.< / p >
< ul >
< li > Default: 50< / li >
< / ul >
2014-08-21 08:07:30 +00:00
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > int< / span > sustainability< / h4 >
< p > Number of visible food sources needed before a creature will eat.< / p >
< ul >
< li > Default: 2< / li >
< li > Range: (0, 16 × < a data-scroll href = "#actionRadius" > actionRadius< / a > - 8]< / li >
< / ul >
2014-08-21 08:07:30 +00:00
< / li >
< li >
2014-08-22 23:27:50 +00:00
< h4 > < span class = "token keyword" > function< / span > wait< / h4 >
< p > What happens when a creature waits.< / p >
< ul >
2014-08-26 06:23:40 +00:00
< li > Default: Creatures lose 5 energy. No effect for cellular automata.< / li >
2014-08-22 23:27:50 +00:00
< / ul >
< / li >
< li id = "customOptions" >
< h4 > < span class = "token keyword" > *< / span > *< / h4 >
< p > The best part about creatures is that you can add < em > whatever you want< / em > to them! In addition to overriding any of the above properties, creatures will accept any methods and properties you throw at 'em.< / p >
2014-08-21 08:07:30 +00:00
< / li >
2014-08-17 03:23:48 +00:00
< / ul >
2014-08-26 06:23:40 +00:00
< p > The second argument to the terra.registerCreature and terra.registerCA is the init function. This function is run within a creature's constructor and allows you to set different attributes for individual creatures. For example, in the < a data-scroll href = "#cyclic" > Cyclic Cellular Automaton< / a > example above we see the following init function:< / p >
2014-08-22 23:27:50 +00:00
< pre > < code class = "language-javascript" > function () {
this.state = Math.floor(Math.random() * 16);
});< / code > < / pre >
< p > Whenever a new creature is created of type 'cyclic', it will be randomly assigned a state of 0 to 15.< / p >
< h2 id = "terrarium" > Terrarium< / h2 >
< p > Terrariums are where the action happens. They're initialized with the following constructor:< / p >
2014-08-25 20:49:28 +00:00
< pre > < code class = "language-javascript" > //new terra.Terrarium(width, height, id, cellSize, insertAfter);
//example: create a 4x4 terrarium called #myTerrarium after element #bugStory
var t = new terra.Terrarium(4, 4, 'myTerrarium', 15, document.getElementById('bugStory');< / code > < / pre >
2014-08-22 23:27:50 +00:00
< h3 > Required< / h3 >
2014-08-21 08:07:30 +00:00
< ul class = "defaults" >
2014-08-22 23:27:50 +00:00
< li >
< h4 > < span class = "token keyword" > int< / span > width< / h4 >
< p > Number of cells in the x-direction.< / p >
< / li >
< li >
< h4 > < span class = "token keyword" > int< / span > height< / h4 >
< p > Number of cells in the y-direction.< / p >
< / li >
2014-08-17 03:23:48 +00:00
< / ul >
2014-08-22 23:27:50 +00:00
< h3 > Optional< / h3 >
< ul class = "defaults" >
< li >
< h4 > < span class = "token keyword" > string< / span > id< / h4 >
< p > id assigned to the generated canvas.< / p >
< / li >
< li >
< h4 > < span class = "token keyword" > int< / span > cellSize< / h4 >
< p > Pixel width of each cell.< / p >
< ul >
< li > Default: 10< / li >
< / ul >
< / li >
< li >
< h4 > < span class = "token keyword" > string< / span > insertAfter< / h4 >
< p > id of the element to insert the canvas after.< / p >
< ul >
< li > Default: canvas is appended to < code class = "language-markup" > document.body< / code > < / li >
< / ul >
< / li >
< / ul >
2014-08-23 23:43:40 +00:00
< p id = "terrariumMethods" > Once initialized, terrariums have a few exposed methods. Using our terrarium < code class = "language-javascript" > t< / code > that we just created:< / p >
2014-08-25 20:49:28 +00:00
< pre > < code class = "language-javascript" > //initial setup
var a = 'a', b = 'b';
2014-08-26 06:23:40 +00:00
terra.registerCreature({type: a});
terra.registerCreature({type: b});
2014-08-25 20:49:28 +00:00
/* makeGrid(content)
*
* Returns a grid populated using a function, 2-d array, or uniform type */
// example: fill the terrarium with the 'b' creature type
t.grid = t.makeGrid(b);
// example: fill the terrarium with a checkerboard pattern
t.grid = t.makeGrid(function (x, y) {
return (x + y) % 2 ? a : b;
});
2014-08-22 23:27:50 +00:00
2014-08-25 20:49:28 +00:00
// example: fill the terrarium's left half with 'a', right half with 'b'
t.grid = t.makeGrid([
[a, a, b, b],
[a, a, b, b],
[a, a, b, b],
[a, a, b, b]
]);
/* makeGridWithDistribution(distribution)
*
* Returns a grid populated randomly with a set creature distribution, where distribution
* is an array of arrays of the form [string 'creatureName', float fillPercent] */
// example: fill the terrarium randomly with approximately half 'a' and half 'b'
t.grid = t.makeGridWithDistribution([[a, 50], [b, 50]]);
/* step(steps)
*
* Returns the next step of the simulation, or the grid after < steps> steps if specified */
// example: advance the terrarium 10 steps in the future
t.grid = t.step(10);
/* draw()
*
* Updates the terrarium's canvas to reflect the current grid */
// example: display all the work we've done above
2014-08-22 23:27:50 +00:00
t.draw();
2014-08-25 20:49:28 +00:00
/* animate(steps, fn)
*
* Starts animating the simulation. The simulation will stop after < steps> steps
* if specified, and call < fn> as a callback once the animation finishes. */
// example: animate the terrarium
t.animate();
/* stop()
*
* stops a currently running simulation */
// example: stop the animation that we just started
t.stop();< / code > < / pre >
2014-08-17 03:23:48 +00:00
2014-08-22 23:27:50 +00:00
< p > Still want more? Check out the source on < a href = "https://github.com/rileyjshaw/terra" > GitHub< / a > !< / p >
2014-08-17 03:23:48 +00:00
< / 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 = "terra.demo.min.js" > < / script >
2014-03-10 03:57:59 +00:00
< / body >
< / html >