s='Uvar n=document.getElementById("c"),fwidth=32rheight=20k=f*r,sgetContext("2d"),t=s.geTf,r),o=t.data,j=[-f,-f+1,1,f+1,f,f , ,-f ],u=c=[],p=[0],e,g,h,q,b,m,i,l,a,d;f=Math.random;j=j.concat(j);n.style.zoom=2;#k*4Jo[A;#=AJ{p[256VA-2*a|a*2P0:K|65280|0;p[512|p[768V2056|a*2:K|KPR71R80}#kJc[0;#2E3Q65536|A*f();#128Q196608|7*f()PA;setIntervalUe=0;u^=R;NHg=WSh=YA;H(h&R)!~uSh^=R;m=h&15;b=;switch(mScaX1:b+=2ZL=2)H!Fd]]SFd]G1;b=R}}c[eG_2:b-=12;W=b<1?0:h|_3:b-=5;Hb>0Sq=A;i= L+Sg=F]];m=Y15;Hm!~3)Hm~=1Si=&7;b+=Z;g=F]];H(Y15)!~3Sb=128;F]G(&7)P32}}O}elXHi~= )i=}Hi> )Fi]G(i&7)Pb;W=h |b}elXW=65537;O}}}e=(e+659)%k}Nd=a*4;g=c[a];l=p[3840|]@@@}s.puTt,0)},40)})();';eval("eval(s_<<R_=n._a]=_g&A_g>>8&_i+5_q+d_0,_ -1_~==__b;O;caX_Z;Hb>ASb=A_Yg>>R&_Xse _Wc[e]_V|a]=a<128?_U(function(S_TtImageData(_S){_R16_QJc[k*f()|0]=_P<<8|_Obreak_Nfor(a=k;a--;){_L;for(d=0;d<8;d+_K(a-128)*2_J;a++)_Hif(_G]=h<<16|_Fc[e+j[_A255_@;o[d++]=l&255;l>>=8_#for(a=0;a<)".replace(/_(.)([^_]+)(?!$)/g,".replace(/$1/g,'$2')"))
/* A cellular automaton based simulation of plants and worms in flatland.
Remake of an old assembler version for the js1k.com demo contest in 2010.
The world is a matrix of 320 * 200 cells, organized as an array of 64000 numeric
values. The value of a cell is interpreted as a 24 bit integer, whose bits
encode its state:
MSB LSB
universe cellctrl cellvalue
76543210 76543210 76543210
|||||||| |||||||| ++++++++--> energy level (and color index)
|||||||| +++--> worm: direction (other: unused)
|++++--> cell type (and palette number)
+--> tickbit
The left and right sides of the world are connected, so that it forms a cylinder
(no torus to save a few bytes and cycles).
Every time interval the automaton processes each cell once. The state of a cell
depends on its own state and the states of cells in the neighborhood. As an
extension, a cell may also influence its neighbors. A global clock bit prevents
the processing of new cells during the same time interval.
The simulation currently knows 4 types of cells:
0) universe cells
Currently, only empty space is defined that does nothing.
1) plant cells
A plant cell gains energy per time tick. If its energy is at maximum and there
are unoccupied cells in the main four directions it spawns child plant cells,
spending energy in the process. Otherwise, it simply stays at maximum energy.
2) worm tail cells
Worm tail cells exist mainly for aesthetic reasons, so that worms leave a blue
t(r)ail. A tail cell simply loses energy each time tick and vanishes reaching 0.
3) worm head cells
A worm head cell consumes energy every time tick. When reaching 0, the worm
dies, leaving an undigested plant cell to keep things balanced. Otherwise,
the worm looks around its neighbor cells in order to find a plant to eat.
If he finds one, he changes his direction towards the plant and absorbs its
energy. If reaching max energy, he tries to spawn a child worm in a neighbor
cell, reducing its energy. A worm cannot stand still and moves to an adjacent
cell, keeping his direction if possible, leaving a worm tail cell with his
current energy level, thereby painting streaks of blue across the canvas.
Have fun watching,
Sebastian.
*/
(function(){
//========================
//== initialize variables
//========================
var
// ---> simulation parameters
// plant specific constants
plantGrowth = 2, // energy gain per time tick
newPlantEnergy = 1, // energy of new child plant cells
remPlantEnergy = 16, // energy of remaining parent plant cell
// worm specific constants
wormHeadDecay = 5, // amount of energy consumed per time tick
wormTailDecay = 12, // decay rate of the worm t(r)ail
newWormEnergy = 32, // energy of a child worm
remWormEnergy = 128, // remaining energy of the parent worm
// <---
// matrix, canvas, context, image and pixel data
canvas = document.getElementById("c"),
width = canvas.width = 320, // width of the matrix
height = canvas.height = 200, // height of the matrix
number = width*height, // total number of cells in the matrix
step = 659, // stepping of the computation (modulo number)
context = canvas.getContext("2d"),
imageData = context.getImageData(0, 0, width, height),
pixelData = imageData.data,
// relative offsets of cells in the neighborhood: N NE E SE S SW W NW
directions = [-width, -width+1, 1, width+1, width, width-1, -1, -width-1],
// global clock of the universe
tickbit = 0, // toggles between 0 and 16
// the matrix
cells = [], // one-dimensional array of cell values
// color palette
palette = [0], // array of blue/green/red number values
// variables used in the computation
index, // current cell index
cell, // current cell number value
metactrl, // value of the universe byte
cellctrl, // value of the cell control byte
cellvalue, // energy level of the cell
celltype, // type of cell
newdirection, // worm variable: best direction to go to
bgr, // blue/green/red palette value (dont ask :))
i, j, // some loop variables
// local variables for often needed values / functions
_255 = 255,
random = Math.random;
//======================
//== initialize program
//======================
// duplicate directions, allows easier indexing
directions = directions.concat(directions);
// zoom the canvas, but no source bytes left to do it nicely...
canvas.style.zoom = 2; // at least works in chrome.
// initialize pixels to opaque white, no need to handle alpha channel later
for (i=0; i<number*4; i++){
pixelData[i] = _255;
}
// initialize palette - lookups 7 times faster than with naive [r,g,b] subarrays
for (i=0; i<=_255; i++){
// plant colors
palette[1<<8|i] = (i<128) ?
_255-2*i | i*2 << 8 | 0: // red -> brown -> green
(i-128)*2 | _255 << 8| 0; // green --> yellow
// worm colors, same for head and tail cells
palette[2<<8|i] =
palette[3<<8|i] = (i<128) ?
8 | 8 << 8 | i*2 << 16 : // dark -> bright blue
(i-128)*2 | (i-128)*2 << 8 | _255<<16; // -> white
}
// initialize matrix
for (i=0; i<number; i++){
cells[i] = 0;
}
for (i=0; i<2000; i++){ // many plants at random energy levels = colors
cells[number*random()|0] = 1<<16 | _255*random();
}
for (i=0; i<128; i++){ // some worms at maximum energy for a head start
cells[number*random()|0] = 3<<16 | 7*random()<<8 | _255;
}
//=============================================
//== algorithm for a time step of the universe
//=============================================
function timestep(){
index = 0; // index of the current cell in the cells array
tickbit ^= 16; // next time tick
i = number; // handle all cells
while (i--){
cell = cells[index];
if (cell) { // skip empty space fast
metactrl = cell >> 16 & _255; // contains cell tick & type
if ((metactrl & 16) !== tickbit){ // skip cells already handled
metactrl ^= 16; // toggle tick bit
celltype = metactrl & 15;
cellvalue = cell & _255;
switch (celltype) { // run type specific cell algorithm
//case 0: // universe
// break;
case 1: // plant
cellvalue += plantGrowth; // the plant grows
if (cellvalue > _255){ // maximum reached?
cellvalue = _255; // cap at maximum
for (j=0; j<8; j+=2){
// look in all main directions for free cells
if (!cells[index+directions[j]]){
// empty space found, spawn a child cell
cells[index+directions[j]] =
metactrl<<16 | newPlantEnergy;
// consume energy for the spawning
cellvalue = remPlantEnergy;
}
}
}
// write back cell value
cells[index] = metactrl<<16 | cellvalue;
break;
case 2: // worm tail
cellvalue -= wormTailDecay;
cells[index] = (cellvalue < 1) ?
0: // tail cell vanishes and leaves empty space
metactrl<<16 | cellvalue;
break;
case 3: // worm head
cellvalue -= wormHeadDecay; // worm uses energy
if (cellvalue > 0){
// worm liveth
cellctrl = cell >> 8 & _255; // current direction
newdirection = -1;
for (j=0; j<8; j++){ // try all directions
cell = cells[index+directions[cellctrl+j]];
celltype = cell >> 16 & 15;
if (celltype !== 3){ // don't consider blocked directions
if (celltype === 1){ // we found a plant, yay!
newdirection = cellctrl+j & 7; // go there
cellvalue += cell&_255; // absorb plant's energy
if (cellvalue > _255){
cellvalue = _255; // cap at max
// try to spawn an offspring
cell = cells[index+directions[newdirection+5]];
if ((cell >> 16 & 15) !== 3){ // not blocked
cellvalue = remWormEnergy;
cells[index+directions[newdirection+5]] =
metactrl<<16 | (newdirection+5&7)<<8 | newWormEnergy;
}
}
break; // look no further, we found what we wanted
}
else if (newdirection === -1){
// remember the first unblocked direction
newdirection = cellctrl+j;
}
}
}
if (newdirection > -1){
cells[index+directions[newdirection]] = metactrl<<16 | (newdirection&7)<<8 | cellvalue;
}
// else the worm is surrounded and dies, should not happen at all
cells[index] = metactrl-1 << 16 | cellvalue; // leave a worm tail
}
else {
// worm dieth (leaves a plant seed)
cells[index] = 1<<16 | 1;
}
break;
}
}
}
index = (index+step) % number; // next cell
}
//--------------------------------------------
//-- Ok, all cells done, now paint the canvas
//--------------------------------------------
i = number;
while (i--){
j=i*4;
cell = cells[i];
bgr = palette[(cell>>8 & 15<<8) | (cell & _255)];
pixelData[j++] = bgr & _255; bgr>>=8; //b
pixelData[j++] = bgr & _255; bgr>>=8; //g
pixelData[j++] = bgr & _255; bgr>>=8; //r
}
context.putImageData(imageData, 0, 0);
}
//=========================
//== There shall be light!
//=========================
setInterval(timestep, 40); // 25 frames per second
})();