  // copyright (c) 2009, keith drakard
  // this program is distributed under the terms of the creative commons
  // license at http://creativecommons.org/licenses/by-nc-sa/2.0/

  var IMAGESBEFORE= 1; // no of images in html page before the game
  
  // ############### init functions ###################################
  // globals
  var running= 0; var paused= 0;             // game state
  var width= 8; var height= 16;              // board shape 8x16
  var tilex= tiley= 24;                      // tile size

  var heap= new Array(width*height);         // the big stack of pieces we all end up with...
  var remove= new Array(width*height);       // which squares are to be removed
  var id;                                    // timeout id (used for pausing/continuing)

  /*var DOWN= 98; var LEFT= 100; var RIGHT= 102; // numpad key definitions - so if you want to use
  var ROTATE_C= 104; var ROTATE_A= 101;        // different keys, replace these with other
                                               // ASCII values - see play() for more */
  var DOWN= 18; var LEFT= 188; var RIGHT= 190; // new key definitions - so if you want to use
  var ROTATE_C= 65; var ROTATE_A= 90;          // different keys, replace these with other
                                               // ASCII values - see play() for more

  var interval;                              // the rate at which pieces fall
  var level= 0;                              // current level and score needed for next level
  var lev= new Array(0,10,70,140,210,270,340,410);

  function preload() {
    if (document.images) {
      colour= new makeArray(9);
        colour[0].src= "/wp-content/scripts/images/common/blank.gif";
        colour[1].src= "/wp-content/scripts/images/columns/orange.gif";
        colour[2].src= "/wp-content/scripts/images/columns/red.gif";
        colour[3].src= "/wp-content/scripts/images/columns/grey.gif";
        colour[4].src= "/wp-content/scripts/images/columns/green.gif";
        colour[5].src= "/wp-content/scripts/images/columns/purple.gif";
        colour[6].src= "/wp-content/scripts/images/columns/lblue.gif";
        colour[7].src= "/wp-content/scripts/images/columns/yellow.gif";  // feel free to make your own colour scheme ;)

        colour[8].src= "/wp-content/scripts/images/common/notblank.gif";

    } else {
      alert("Sorry, this game needs to run on a browser\nwhich supports JavaScript 1.2\neg. MSIE4+ or NS4+");
    }
  }


  // ############### movement functions ###############################
  function handleKeyPress(evt) { // written by Eric Pascarello
    nbr = (window.Event) ? evt.which : event.keyCode;
    play(nbr);
    return true;
  }
  document.onkeydown= handleKeyPress;

  function play(key) {
    // if you want to redefine the controls and don't know the
    // keycodes of the new keys, uncomment the line below:
    // alert(key);

    if (key== LEFT)     { move(-1); return(1); }
    if (key== RIGHT)    { move(1); return(1); }
    if (key== DOWN)     { move_down(); return(1); }
    if (key== ROTATE_C) { rotate(1); return(1); }
    if (key== ROTATE_A) { rotate(-1); return(1); }

    return(0);
  }

  function rotate(dir) {
    if (block[0]== -1) return(0);

	// all rotating does is swap the colours upwards or downwards:
    if (dir==1) {
	  var temp  = cblock[0];
	  cblock[0] = cblock[1];
	  cblock[1] = cblock[2];
	  cblock[2] = temp;

	} else {
	  var temp  = cblock[2];
	  cblock[2] = cblock[1];
	  cblock[1] = cblock[0];
	  cblock[0] = temp;
	}

	display(0);

    return(1);
  }

  function move(dir) {
    if (block[0]== -1) return(0);

    // check if moving in dir would hit the edge or another block:
    for (var i=0; i<3; i++) {
      edge= (dir==1) ? ((block[i]% width)== width-1) : ((block[i]% width)== 0)
      if (edge || heap[block[i]+dir]) return(0);
    }

    // nope, it's safe to re-display the piece:
    display(dir);
    return(1);
  }

  function move_down() {
    if (block[0]== -1) return(0);

    // check if the piece has hit the bottom row or
    // if it now intersects with the heap of old pieces:
    for (var i=0; i<3; i++) {
      if ((block[i]>= (height-1)*width) || (heap[block[i]+width])) {
        // yep, that's it for this piece:
        merge(); return(0);
      }
    }

    // nope, it's safe to re-display the piece:
    display(width);
    return(1);
  }


  // ############### display function #################################
  function display(modifier) {
    // this is done in two steps for a reason... if you're curious,
    // edit this to do it in one loop and watch the effect :)

    // delete the piece from old position:
    for (var i=0; i<3; i++) {
      document.images[""+(block[i]+IMAGESBEFORE)].src= colour[0].src;
    }

    // and re-display it at the new:
    for (i=0; i<3; i++) {
      block[i]+= modifier;
      document.images[""+(block[i]+IMAGESBEFORE)].src= colour[cblock[i]].src;
    }
  }


  // ############### heap functions ###################################
  function merge() {
    // merge the piece into the heap and clear the old arrays:
    for (var i=0; i<3; i++) {
      heap[block[i]]= cblock[i]; block[i]= -1; cblock[i]= -1;
    }
    paused= 1;
    setTimeout('mycheck()', 100);
  }

  function mycheck() {
    var howmany= check();
    
    if (howmany > 0) {
      del();
      score(howmany);
      setTimeout('mycheck()', 100);
    } else {
      paused= 0;
    }
  }


  function kcheck() {
    var count= 0; var pos;

    for (var i=0; i<3; i++) {
      pos= block[i];
      count+= chk(pos, -1);       // left
      count+= chk(pos, 1);        // right
      count+= chk(pos, width+1);  // bottom right
      count+= chk(pos, width);    // down
      count+= chk(pos, width-1);  // bottom left
      count+= chk(pos, -width+1); // top right
      count+= chk(pos, -width-1); // top left
      //block[i]= -1; cblock[i]= -1;
    }

    return (count);
  }


  function check() {
    var pos; var diff; var count= 0;
  
    // check horizontal
    diff= 1;
    for (var y= 0; y< height; y++) {
      for (var x= 2; x< width; x++) {
        pos= (y*width)+ x;
        count+= chk(pos, diff);
      }
    }

    // check top-left/bottom-right diagonal
    diff= 1+ width;
    // do diagonals along top row
    for (var i= 1; i< width-2; i++) {
      for (var j= 2; j< Math.min(width, width-i); j++) {
        pos= i+ (j*diff);
    	count+= chk(pos, diff);
      }
    }
    // do diagonals along left side
    for (var i= 0; i<= (height-1)*width; i+=width) {
      for (var j= 2; j< Math.min(width, height- (i/width)); j++) {
        pos= i+ (j*diff);
	    count+= chk(pos, diff);
        //	if (i== (height-4)*width) { alert("heap[]= "+heap[pos]+"\nchk()= "+chk(pos,diff)+"\npos= "+pos+"\ni= "+i+", j= "+j); }
      }
    }

    // check vertical
    diff= width;
    for (var x= 0; x< width; x++) {
      for (var y= 2; y< height; y++) {
        pos= (y*width)+ x;
        count+= chk(pos, diff);
      }
    }

    // check top-right/bottom-left diagonal
    diff= width- 1;
    // do diagonals along top row
    for (var i= 2; i< width; i++) {
      for (var j= 2; j< Math.min(width, width-i); j++) {
        pos= i+ (j*diff);
    	count+= chk(pos, diff);
      }
    }
    // do diagonals along right side
    for (var i= width-1; i< height*width; i+=width) {
      for (var j= 2; j< Math.min(width, height- (i/width)); j++) {
        pos= i+ (j*diff);
	    count+= chk(pos, diff);
      }
    }


    return (count); 
  }

  function chk(p, d) {
    var c= 0;

    if (heap[p] == heap[p-d] && heap[p-d] == heap[p-(2*d)] && heap[p] != 0) {
      if (remove[p]      != 1) remove[p]      = 1; c++;
      if (remove[p- d]   != 1) remove[p- d]   = 1; c++;
      if (remove[p-(2*d)]!= 1) remove[p-(2*d)]= 1; c++;
    }

    return (c);
  }

  function del() {
    pause(200);

    for (i=0; i<(width*height); i++) {
      if (remove[i] == 1) {
    	document.images[""+(i+IMAGESBEFORE)].src= colour[0].src;
	    remove[i]= 0; heap[i]= 0;
      }
    }
    
    gravity();
  }
    

  function gravity() {
    // drop the pieces after you've removed some:
    var mark, pos, oldpos;

    for (var x=0; x<width; x++) {
      mark= -1;

      for (var y=(height-1); y>=0; y--) {
    	pos= (y*width)+x;

    	if (mark == -1 && heap[pos] == 0) {
          mark= y;

        } else if (mark > -1 && heap[pos] > 0) {
   	      oldpos= (mark*width)+x;
          document.images[""+(oldpos+IMAGESBEFORE)].src= document.images[""+(pos+IMAGESBEFORE)].src;
    	  heap[oldpos]= heap[pos];
          mark--;
          document.images[""+(pos+IMAGESBEFORE)].src= colour[0].src;
	      heap[pos]= 0;

    	}

      }
    }
	
  }
  

  // ############### scoring functions ################################
  function score(modifier) {
    // add some points to the score, depending on:
	// the number of squares matched
    var cs= (document.user.score.value)* 1;
    cs+= modifier;
    
    // check if we've reached a new level:
    if (cs>=lev[level] && level<8) level++;

    // update score and level displays:
    document.user.score.value= cs;
    document.user.level.value= level;

    // and set the rate that the pieces fall at:
    interval= 400- (10*level);
  }


  // ############### main loop ########################################
  function step() {
    // main loop - if a piece is in play, move it down
    // otherwise, try to make a new one...

    if (block[0]!= -1) {
      // piece in play, so move it down and do this again:
      move_down();
      id= setTimeout('step()', interval);

    } else {
      // no piece in play atm, so see if we can make one:

      if (newpiece()) {
        // new piece has been created, so display it:
        id= setTimeout('step()', interval);

      } else {
        // can't create new piece, so that's it for this game:
        var color= rand(7);
        for (var c=0; c<(width*height); c++) {
          if (heap[c]) document.images[""+(c+IMAGESBEFORE)].src= colour[color].src;
        }
        document.user.b.value= " new game "; running= 0;
        // without setting block to null, we're unable to reset it after the first game...
        block= null; cblock= null;
      }
    }
  }


  // ############### miscellaneous stuff ##########################
  function pause(ms) {
    var when= new Date(); var then= when.getTime()+ms; var now;

    do { when= new Date(); now= when.getTime();
       } while (now < then);
  }

  function newpiece() {
    var m= rand(width);

    // make the piece
    block[0]= 8; block[1]= 8+width; block[2]= 8+(2*width);

    for (var i=0; i<3; i++) {
      // move piece's starting position by the random modifier:
      block[i]-= m;
      // and check if we've hit another piece:
      if (heap[block[i]]) return(0);
    }

	// the colour of the block
    cblock[0]= rand(7); cblock[1]= rand(7); cblock[2]= rand(7);
	
    // otherwise, we've successfully created a new piece:
    for (var i=0; i<3; i++) {
      document.images[""+(block[i]+IMAGESBEFORE)].src= colour[cblock[i]].src;
    }

    return(1); 
  }


  function newgame() {
    if (!running) {
      if (!paused) {

        // reset the board:
        document.user.score.value= 0;
        document.user.level.value= 1;
        block= new Array(2);          // holds position info for piece in play
        cblock= new Array(2);         // holds colour info for piece in play
        for (var i=0; i<3; i++) { block[i]= -1; cblock[i]= -1; }
        for (var c=0; c<(width*height); c++) {
          document.images[""+(c+IMAGESBEFORE)].src= colour[0].src;
          heap[c]= 0; remove[c]= 0;
        }
        newpiece();
        level= 1; interval= 400;
      }
      // unpause game (even if it wasn't paused before)
      running= 1; paused= 0;
      document.user.b.value= " pause ";
      id= setTimeout('step()', interval*2);

    } else {
      // pause game:
      running= 0; paused= 1;
      document.user.b.value= " continue ";
      clearTimeout(id);
    }
  }

  // The Central Randomizer 1.3 (C) 1997 by Paul Houle (houle@msc.cornell.edu)
  // See:  http://www.msc.cornell.edu/~houle/javascript/randomizer.html
  // NOTE:- this example is set up to produce integers between 1-limit
  rnd.today=new Date(); rnd.seed=rnd.today.getTime();
  function rnd() {
    rnd.seed = (rnd.seed*9301+49297) % 233280;
    return rnd.seed/(233280.0);
  }
  function rand(limit) {
    return Math.ceil(rnd()*limit);
  }

  // The following functions were written by Martin Webb at http://www.irt.org/
  function makeArray(n) {
    this.length= n; for (i=0; i<n; i++) { this[i]= new Image(); }
    return this;
  }

  function initialise() {
	var output= '';
	output+= '<form name="user">';
	output+= '<table align=center cellspacing=0 cellpadding=0 border=0>\n';

	// create the board
	output+= '<tr bgcolor="#663399"><td rowspan=7><table cellpadding=8 border=0>\n';
	output+= '<tr bgcolor="#330066"><td><table cellspacing=0 cellpadding=0 border=0>\n';
	for (y=0; y<height; y++) {
	  for (x=0; x<width; x++) {
	    if (x== 0) output+= '<tr>';
	    output+= '<td><img src="/wp-content/scripts/images/common/blank.gif"';
	    output+= ' name="'+((y*width)+x)+'" width='+tilex+' height='+tiley+' alt="" border=0></td>';
	    if (x== width-1) output+= '</tr>\n';
	  }
	}
	output+= '</table></td></tr></table></td><td rowspan=2>&nbsp;&nbsp;&nbsp;</td>\n';

	// create the ui
	// note you need to blur() these otherwise netscape won't detect key presses...
	output+= '<td bgcolor="#663399" valign=top align=center><br>\nscore:&nbsp;';
	output+= '<input type="text" size=4 name="score" value=0 onfocus="blur()">&nbsp;<br><br>\nlevel:&nbsp;';
	output+= '<input type="text" size=2 name="level" value=1 onfocus="blur()"><br><br>\n<input type="button" name="b"';
	output+= ' value=" new game " onClick="newgame()" onfocus="blur()"></td></tr>\n';

	output+= '</table></form>\n';

	document.write(output);
	preload();
  }