//conwayice - kirk israel, http://kisrael.com/
//generations of Conwy's game of life plotted in 3D
//click to reset; the higher the click, the denser the initial population
//camera follows the mouse, if left idle, starts rotating on its own
//core life code loving ripped from Mike Davis:
// http://processing.org/learning/examples/cellularautomata1.html
// (but my version is better commented ;-)


int sx, sy; 
float density = 0.15; 
int[][][] world;
int[][][] worldhistory;
int historylength = 32;
int worldsize=32;
float r = 0;
int bs = 6;

float oldmx, oldmy;
int counter = 500;
int spinpatience = 100;
float rY,rZ;

//reset and redraw on mouseclick, 
//initial density a function of how high they click on the screen
//density is 0 < d < .3 or so
void mousePressed(){
  density = (400.0-mouseY)/1200.0;
        reset(); 
}


//re-initialize arrays, randomy populate, and run all the generations
void reset(){  
  world = new int[sx][sy][2]; 
  worldhistory = new int[sx][sy][historylength]; 
  
    //randomly populate
  for (int i = 0; i < sx * sy * density; i++) { 
    world[(int)random(sx)][(int)random(sy)][1] = 1; 
  } 

  for(int i = 0 ; i < historylength; i++){
    dogen(i);    
  }
}

void setup() 
{ 
  background(128);
  size(400,400,P3D);
  rectMode(CENTER);
  frameRate(20);
  sx =worldsize;
  sy = worldsize;
  reset();
  oldmx = mouseX;
  oldmy = mouseY;
  
} 

void draw(){
  background(128);
  //blue, plus an alpha value for opacity etc...
  fill(32,32,128,30);
    //fill(200,200,255,40);
  noStroke();
  //move to roughly middle of screen
  translate(width/2,height/2);

  //if they've moved the mouse, readjust the rotation
  //and reset the mouse watching and counter
  if(oldmx != mouseX || oldmy != mouseY){
  
  rY = (mouseX)/100.0;
  rZ = (200.0-mouseY)/200.0;
  
    oldmx = mouseX;
  oldmy = mouseY;
  counter = 0;
  } else {
    counter++;
    //if its been a while since they've moved the mouse,
    //rotate it automagically
    if(counter > spinpatience){
      rY += .02;
    }
  }
    rotateY(rY);
  rotateZ(rZ);

  r += .01;
  for(int gen = 0; gen <historylength; gen++){
    for(int x = 0 ; x < sx; x++){
      for(int y = 0; y < sy; y++){
        if(worldhistory[x][y][gen] == 1){
          pushMatrix();
          translate(((sx*-.5)+x)*bs,(historylength*(bs/2)) - (gen*6),((sy*-.5)+y)*bs);
          box(bs,bs,bs); 
          popMatrix();
        }
      }
    }
  }
}




// Count the number of adjacent cells 'on' 
int neighbors(int x, int y) 
{ 
  return world[(x + 1) % sx][y][0] + 
    world[x][(y + 1) % sy][0] + 
    world[(x + sx - 1) % sx][y][0] + 
    world[x][(y + sy - 1) % sy][0] + 
    world[(x + 1) % sx][(y + 1) % sy][0] + 
    world[(x + sx - 1) % sx][(y + 1) % sy][0] + 
    world[(x + sx - 1) % sx][(y + sy - 1) % sy][0] + 
    world[(x + 1) % sx][(y + sy - 1) % sy][0]; 
} 

void dogen(int historyptr)
{ 
  for (int x = 0; x < sx; x=x+1) { 
    for (int y = 0; y < sy; y=y+1) { 
      //if cell TOBE born, or cell NOWIS alive and TOBO no change...
      if ((world[x][y][1] == 1)|| (world[x][y][0] == 1 && world[x][y][1] == 0) ) { 
        //NOWIS ALIVE
        world[x][y][0] = 1; 
        worldhistory[x][y][historyptr] = 1;
      } else {
         worldhistory[x][y][historyptr] = 0;
      }
      //if cell TOBE killed  
      if (world[x][y][1] == -1) { 
        //NOWIS dead
        world[x][y][0] = 0; 
      } 
      //TOBE nochange...
      world[x][y][1] = 0; 
    } 
  }
  // Birth and death cycle 
  for (int x = 0; x < sx; x=x+1) { 
    for (int y = 0; y < sy; y=y+1) { 
      int count = neighbors(x, y); 
      //if 3 neighbors and NOWIS dead...
      if (count == 3 && world[x][y][0] == 0) 
      { 
        //TOBE born
        world[x][y][1] = 1; 
      } 
      //if too few, too many neighbors and NOWIS alive
      if ((count < 2 || count > 3) && world[x][y][0] == 1) 
      { 
        //TOBE killed
        world[x][y][1] = -1; 
      } 
    } 
  } 
}