// Simple cellular-automata based water simulation,
// http://iobound.com/2010/08/js1k/

// Based on Hugo Elias' algorithm at: 
// http://freespace.virgin.net/hugo.elias/graphics/x_water.htm

// Variable lookup:
//
// c  - Canvas
// d  - 2D Context
// w  - Pixels across (100)
// p  - Total pixels (100*50)
// m  - First heightmap buffer
// n  - Second heightmap buffer
// t  - Texture buffer
// x,y,z  - Temp vars

// Function lookup:
//
// W  - Perturb water
// F  - Math.floor
// R  - Math.random

// To strip comments/whitespace (hat tip to Paul Hammond, http://phmmnd.me/1k):
// perl -p -0 -e 's!(//[^\n]*)?\n\s*!!g;s!;}!}!g;s!;+!;!g' <script.js >compressed.js

// Initialise variables
var c=document.body.children.c,
  d=c.getContext('2d'),
  w=100,
  z=p=5E3,
  x,y,
  t=[],m=[],n=[],
  F=Math.floor,
  R=Math.random;

// Use alpha to take edge off pixellyness
for(d.globalAlpha=0.8;z--;)
  // Initialise background checkerboard texture, and clear water
  // buffers (using texture vals, as close enough to zero)
  t[z]=m[z]=n[z]=z%2E3<1E3?z%20<10:z%20>9;

// Tick the simulation and render
setInterval(function(){

  // Update water height map - see algorithm for explanation
  for(x=z=p;x--;)

    // Calculate new value based on previous 2 frames
    n[x]=(m[(p+x-1)%p]+
            m[(x+1)%p]+
            m[(p+x-w)%p]+
            m[(x+w)%p]>>1)-n[x],

    // Apply damping
    n[x]-=n[x]>>5
  ;

  // Render pixels
  for(;z--;)

    // Get X and Y wave gradient by comparing height with neighbours
    x=m[(p+z-1)%p]-m[(z+1)%p]>>9,
    y=m[(p+z-w)%p]-m[(z+w)%p]>>9,

    // Use X and Y gradients as offsets into texture array
    // (Fake refraction)
    y=t[((w*y)+x+z+p)%p]*9,

    // Use X gradient to pick a reflection map value
    // (Fake environment map)
    x=(x<0?8:x>7?15:x+8).toString(16),
    
    // Use these components to make a bluish hex value
    d.fillStyle='#'+x+y+x+'ff'+y,

    // Draw a 3x3 pixel
    d.fillRect(z%w*3,F(z/w)*3,3,3)
  ;

  // Swap water buffers over
  x=m;
  m=n;
  n=x

},20);

// Perturb water at position i
function W(i){

  // Push down a cross of pixels centered on i
  m[i+1]=m[i-1]=m[i+w]=m[i-w]=m[i]-=p
}

// Register mouse handler to stir water
c.onmousemove=function(e){
  W(F(e.offsetX/3)+w*F(e.offsetY/3))
};

// Make it rain
(function _(){
  W(F(R()*p));
  setTimeout(_,F(R()*p))
})()

