Code viewer for World: Pacman
//MY WORLD IS AN ATTEMPT AT PACMAN. I STRUGGLED TO GET THIS WORKING COMPLETLY ALTHOUGH IT DOES HAVE SOME FUNCTIONS.
//THE PACMAN IS SELF MOVED USING THE ARROW KEYS.
//IT REMOVES THE FIRST DOT BUT AFTER THAT I CAN NOT GET IT TO EAT THE REST.
//IF A GHOST TOUCHES THE PACMAN HE WILL DIE AND THE GAME WILL END.


// World must define these:
const	 	CLOCKTICK 	= 250;					// speed of run - move things every n milliseconds
const		MAXSTEPS 	= 200;					// length of a run before final score


const  SCREENSHOT_STEP = 50;    


//---- global constants: -------------------------------------------------------
const gridsize =20;						// number of squares along side of world

const NOBOXES =  Math.trunc ( (gridsize * gridsize) /  25);
		// density of maze - number of internal boxes
		// (bug) use trunc or can get a non-integer

const squaresize = 10;					// size of square in pixels
const MAXPOS = gridsize * squaresize;		// length of one side in pixels

const SKYCOLOR 	= 0x000000;				// a number, not a string
const BLANKCOLOR = 0x0000FF ;			// make objects this color until texture arrives (from asynchronous file read)




const show3d = false;						// Switch between 3d and 2d view (both using Three.js)

const startRadiusConst	 	= MAXPOS * 0.8 ;		// distance from centre to start the camera at
const skyboxConst			= MAXPOS * 10;		// where to put skybox
const maxRadiusConst 		= MAXPOS * 10;		// maximum distance from camera we will render things

//--- Mind can pick one of these actions -----------------

const ACTION_LEFT 		= 0;
const ACTION_RIGHT 		= 1;
const ACTION_UP 		= 2;
const ACTION_DOWN 		= 3;
const ACTION_STAYSTILL 	= 4;

// in initial view, (smaller-larger) on i axis is aligned with (left-right)
// in initial view, (smaller-larger) on j axis is aligned with (away from you - towards you)

// contents of a grid square

const GRID_BLANK 	= 0;
const GRID_WALL 	= 1;
const GRID_MAZE 	= 2;
const GRID_CIRCLE   = 3;

// --- some useful random functions  -------------------------------------------

function randomfloatAtoB ( A, B )
{
 return ( A + ( Math.random() * (B-A) ) );
}

function randomintAtoB ( A, B )
{
 return  ( Math.round ( randomfloatAtoB ( A, B ) ) );
}

function randomBoolean()
{
 if ( Math.random() < 0.5 ) { return false; }
 else { return true; }
}z



//---- start of World class -------------------------------------------------------

function World() {
// most of World can be private
// regular "var" syntax means private variables:
var BOXHEIGHT;		// 3d or 2d box height
var GRID 	= new Array(gridsize);			// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array
var WALLS 	= new Array ( 4 * gridsize );		// need to keep handles to wall and maze objects so can find them later to paint them
var MAZE 	= new Array ( NOBOXES );
var CIRCLE  = new Array (gridsize  );

var theagent, theenemy, theenemyT, theenemyP, theenemyO ;
// enemy and agent position on squares
var ei, ej, ai, aj, eTi, eTj, ePi, ePj, eOi, eOj ;
var badsteps;
var goodsteps;
var step;
var self = this;						// needed for private fn to call public fn - see below
// regular "function" syntax means private functions:

function initGrid()
{
 for (var i = 0; i < gridsize ; i++)
 {
    //CANT RUN WITHOUT
    //CREATING GRID
  GRID[i] = new Array(gridsize);		// each element is an array
 for (var j = 0; j < gridsize ; j++)
 {
  GRID[i][j] = GRID_BLANK;		// each element is an array
 }
 }
}

function occupied ( i, j )		// is this square occupied
{
 if ( ( ei == i ) && ( ej == j ) ) return true;		// variable objects
 if ( ( ai == i) && ( aj == j ) ) return true;
 if ( ( eTi == i ) && ( eTj == j ) ) return true;
 if ( ( eOi == i ) && ( eOj == j ) ) return true;
 if ( ( ePi == i ) && ( ePj == j ) ) return true;
 
//WALL
 if ( GRID[i][j] == GRID_WALL ) return true;		// fixed objects
 if ( GRID[i][j] == GRID_MAZE ) return true;
 //if ( GRID[i][j] == GRID_CIRCLE ) return true;

 return false;
}

// logically, coordinates are: y=0, x and z all positive (no negative)
// logically my dimensions are all positive 0 to MAXPOS
// to centre everything on origin, subtract (MAXPOS/2) from all dimensions

function translate ( x )
{
 return ( x - ( MAXPOS/2 ) );
}

//--- skybox --------------------------------------------------------------------------------------------

function initSkybox()
{

    var materialArray = [
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/suzannecampbell/black.jpg" ), side: THREE.BackSide } ) )
 	];
  var skyGeometry = new THREE.CubeGeometry ( skyboxConst, skyboxConst, skyboxConst );
  var skyMaterial = new THREE.MeshFaceMaterial ( materialArray );
  var theskybox = new THREE.Mesh ( skyGeometry, skyMaterial );
  threeworld.scene.add( theskybox );						// We are inside a giant cube
}

// --- asynchronous load textures from file ----------------------------------------
// credits:
// http://commons.wikimedia.org/wiki/File:Old_door_handles.jpg?uselang=en-gb
// https://commons.wikimedia.org/wiki/Category:Pac-Man_icons
// https://commons.wikimedia.org/wiki/Category:Skull_and_crossbone_icons
// http://en.wikipedia.org/wiki/File:Inscription_displaying_apices_(from_the_shrine_of_the_Augustales_at_Herculaneum).jpg

// loader return can call private function
function loadTextures()
{
	//PACMAN

 var loader3 = new THREE.TextureLoader();
 loader3.load ( '/uploads/suzannecampbell/pacmanguy.png',	function ( thetexture ) {
		thetexture.minFilter = THREE.LinearFilter;
		theagent.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );

	   //GHOST
 var loader4 = new THREE.TextureLoader();
 loader4.load ('/uploads/suzannecampbell/pacmanghostred.jpeg',	function ( thetexture ) {
		thetexture.minFilter = THREE.LinearFilter;
		theenemy.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );
	var loader2 = new THREE.TextureLoader();
 loader2.load ('/uploads/suzannecampbell/panmanghostblue.jpg',	function ( thetexture ) {
		thetexture.minFilter = THREE.LinearFilter;
		theenemyT.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );
		var loader1 = new THREE.TextureLoader();
 loader1.load ('/uploads/suzannecampbell/pacmanghostpink.png',	function ( thetexture ) {
		thetexture.minFilter = THREE.LinearFilter;
		theenemyP.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );
	var loader5 = new THREE.TextureLoader();
 loader5.load ('/uploads/suzannecampbell/pacmanghostorange.jpeg',	function ( thetexture ) {
		thetexture.minFilter = THREE.LinearFilter;
		theenemyO.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );
}

// --- add fixed objects ----------------------------------------

 //OUTER WALL..
function initLogicalWalls()		// set up logical walls in data structure, whether doing graphical run or not
{
 for (var i = 0; i < gridsize ; i++)
  for (var j = 0; j < gridsize ; j++)
   if ( ( i===0 ) || ( i==gridsize-1 ) || ( j===0 ) || ( j==gridsize-1 ) )
   {
    	GRID[i][j] = GRID_WALL ;
   }
}


function initThreeWalls()		// graphical run only, set up blank boxes, painted later
{
 var t = 0;
 for (var i = 0; i < gridsize ; i++)
  for (var j = 0; j < gridsize ; j++)
   if ( GRID[i][j] == GRID_WALL )
   {
 	 var shape    = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 	 var thecube  = new THREE.Mesh( shape );
	 thecube.material.color.setHex( BLANKCOLOR  );

    //SETS UP OUTER..
    	 thecube.position.x = translate ( i * squaresize );   		// translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
    	 thecube.position.z = translate ( j * squaresize );
    	 thecube.position.y =  0;

 	 threeworld.scene.add(thecube);
	 WALLS[t] = thecube;				// save it for later
	 t++;
   }
}


function paintWalls ( material )
{
 for ( var i = 0; i < WALLS.length; i++ )
 {
   if ( WALLS[i] )  WALLS[i].material = material;
 }
}

//-----------------------------------------------------------------

function initLogicalCircle()		// set up logical walls in data structure, whether doing graphical run or not
{
 for (var i = 0; i < gridsize ; i++)
 {
  for (var j = 0; j < gridsize; j++)
  {
       if( !occupied(i,j))
       {
          GRID[i][j] = GRID_CIRCLE; 

       }
  }
 }
}

function initThreeCircle()		// graphical run only, set up blank boxes, painted later
{
 var t = 0;
 for (var i = 0; i < gridsize  ; i++)
 {
  for (var j = 0; j < gridsize ; j++)
  {
   if ( GRID[i][j] == GRID_CIRCLE )
   {
       if((i == ai)&&(j==aj))
       {
           GRID[ai][aj] = GRID_BLANK;
       }
       
	 var shape    = new THREE.SphereGeometry(2,2,2 );
 	 var thecircle  = new THREE.Mesh( shape );
	 thecircle.material.color.setHex( 0xFFFFFF  );
	 
    //SETS UP OUTER..
    
    	 thecircle.position.x = translate ( i * squaresize );   		// translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
    	 thecircle.position.z = translate ( j * squaresize );
    	 thecircle.position.y =  0;
 	 threeworld.scene.add(thecircle);
	 CIRCLE[t] = thecircle;				// save it for later
	 t++;
	 
	 
   }
}
}
}
function paintCircles ( material )
{
 for ( var i = 0; i < CIRCLE.length; i++ )
 {
   if ( CIRCLE[i] )  CIRCLE[i].material = material;
 }
}

//---------------------------------------------------------


//one square
function initLogicalMaze()
{
 for ( var c=1 ; c <= NOBOXES ; c++ )
 {
  	var row = randomintAtoB(1,gridsize-2);	// inner squares are 1 to gridsize-2
  	var col = randomintAtoB(1,gridsize-2);
    	//GRID[row][col] = GRID_MAZE ;
    	for(var i = 1; i<3; i++){
    	    GRID[i][2] = GRID_MAZE ;
            GRID[i][16] = GRID_MAZE ;
    	}
    	for(var a=1; a<4; a++){
            GRID[a][8] = GRID_MAZE ;
            GRID[a][10] = GRID_MAZE ;
    	}

        GRID[8][8] = GRID_MAZE ;
        GRID[9][8] = GRID_MAZE ;
        GRID[11][8] = GRID_MAZE ;
        GRID[12][8] = GRID_MAZE ;
        GRID[8][9] = GRID_MAZE ;
        GRID[12][9] = GRID_MAZE ;
        GRID[8][10] = GRID_MAZE ;
        GRID[12][10] = GRID_MAZE ;
        for(var i = 8; i<13; i++){
            GRID[i][11] = GRID_MAZE;
        }
        GRID[5][2] = GRID_MAZE ;
        GRID[6][2] = GRID_MAZE ;
        GRID[7][2] = GRID_MAZE ;
        GRID[10][1] = GRID_MAZE ;
        GRID[10][2] = GRID_MAZE ;
        GRID[10][3] = GRID_MAZE ;

        GRID[13][2] = GRID_MAZE ;
        GRID[14][2] = GRID_MAZE ;
        GRID[15][2] = GRID_MAZE ;

        GRID[17][2] = GRID_MAZE ;
        GRID[18][2] = GRID_MAZE ;

        GRID[5][5] = GRID_MAZE ;
        GRID[5][6] = GRID_MAZE ;
        GRID[5][7] = GRID_MAZE ;
        GRID[5][8] = GRID_MAZE ;
        GRID[6][6] = GRID_MAZE ;
        GRID[3][14] = GRID_MAZE ;
        GRID[4][14] = GRID_MAZE ;
        GRID[5][14] = GRID_MAZE ;
        GRID[5][15] = GRID_MAZE ;
        GRID[5][16] = GRID_MAZE ;
       /* for(var i =3;i<8; i++)
        {
            GRID[i][1] = GRID_MAZE ;
        }

        GRID[6][17] = GRID_MAZE ;
        */
        for(var i =8;i<13; i++)
        {
            GRID[i][16] = GRID_MAZE ;
        }

        GRID[10][17] = GRID_MAZE ;
        //for(var i =3;i<6; i++)
        //{
          //  GRID[i][18] = GRID_MAZE ;
        //}


        GRID[3][18] = GRID_MAZE ;
        
         GRID[15][14] = GRID_MAZE ;
        GRID[16][14] = GRID_MAZE ;
        GRID[15][15] = GRID_MAZE ;
        GRID[15][16] = GRID_MAZE ;
        GRID[17][14] = GRID_MAZE ;
        
         GRID[18][10] = GRID_MAZE ;
          GRID[17][10] = GRID_MAZE ;
           GRID[16][10] = GRID_MAZE ;
           
           GRID[18][8] = GRID_MAZE ;
          GRID[17][8] = GRID_MAZE ;
           GRID[16][8] = GRID_MAZE ;
        GRID[17][17] = GRID_MAZE ;
        GRID[18][17] = GRID_MAZE ;
        //GRID[17][14] = GRID_MAZE ;
       // GRID[4][14] = GRID_MAZE ;
        //GRID[3][18] = GRID_BLANK;
        



 }
}


function initThreeMaze()
{
 var t = 0;
 for (var row = 0; row < gridsize ; row++)
  for (var col = 0; col < gridsize ; col++)
   if ( GRID[row][col] == GRID_MAZE )
   {
   	var shape    = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
  	var thecube  = new THREE.Mesh( shape );
	thecube.material.color.setHex( BLANKCOLOR  );

  	thecube.position.x = translate ( row * squaresize );
  	thecube.position.z = translate ( col * squaresize );
  	thecube.position.y =  0;

 	threeworld.scene.add(thecube);
	MAZE[1] = thecube;		// save it for later
	t++;
   }
}


function paintMaze ( material )
{
 for ( var i = 0; i < MAZE.length; i++ )
 {
   if ( MAZE[i] )  MAZE[i].material = material;
 }
}


// --- enemy functions -----------------------------------


function drawEnemy()	// given ei, ej, draw it
{
  var x = translate ( ei * squaresize );
  var z = translate ( ej * squaresize );
  var y =  0;

 theenemy.position.x = x;
 theenemy.position.y = y;
 theenemy.position.z = z;
 threeworld.scene.add(theenemy);

 threeworld.follow.copy ( theenemy.position );		// if camera moving, look back at where the enemy is
}


function initLogicalEnemy()
{
//ENEMY START LOCAtion:
 var i, j;
 do
 {
  i = 9;//randomintAtoB(1,gridsize-2);
  j = 9;//randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square

 ei = i;
 ej = j;
}


function initThreeEnemy()
{
 var shape    = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 theenemy = new THREE.Mesh( shape );
 theenemy.material.color.setHex( BLANKCOLOR  );
 drawEnemy();
}


function moveLogicalEnemy(a)
{
// move towards agent
// put some randomness in so it won't get stuck with barriers

 var i = ei;
 var j = ej;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;

 if ( ! occupied(i,j) )
 {
  ei = i;
  ej = j;
 }
}



// --- enemy TWO functions -----------------------------------
//BLUE

function drawEnemyT()	// given ei, ej, draw it
{
  var x = translate ( eTi * squaresize );
  var z = translate ( eTj * squaresize );
  var y =  0;

 theenemyT.position.x = x;
 theenemyT.position.y = y;
 theenemyT.position.z = z;
 threeworld.scene.add(theenemyT);

 threeworld.follow.copy ( theenemyT.position );		// if camera moving, look back at where the enemy is
}


function initLogicalEnemyT()
{
//ENEMY START LOCAtion:
 var i, j;
 do
 {
  i = 9;//randomintAtoB(1,gridsize-2);
  j = 10;//randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square

 eTi = i;
 eTj = j;
}


function initThreeEnemyT()
{
 var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 theenemyT = new THREE.Mesh( shape );
 theenemyT.material.color.setHex( BLANKCOLOR  );
 drawEnemyT();
}


function moveLogicalEnemyT(a)
{
// move towards agent
// put some randomness in so it won't get stuck with barriers

 var i = eTi;
 var j = eTj;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;

 if ( ! occupied(i,j) )
 {
  eTi = i;
  eTj = j;
 }
}

// --- enemy THREE functions -----------------------------------
//PINK

function drawEnemyP()	// given ei, ej, draw it
{
  var x = translate ( ePi * squaresize );
  var z = translate ( ePj * squaresize );
  var y =  0;

 theenemyP.position.x = x;
 theenemyP.position.y = y;
 theenemyP.position.z = z;
 threeworld.scene.add(theenemyP);

 threeworld.follow.copy ( theenemyP.position );		// if camera moving, look back at where the enemy is
}


function initLogicalEnemyP()
{
//ENEMY START LOCAtion:
 var i, j;
 do
 {
  i = 10;//randomintAtoB(1,gridsize-2);
  j = 10;//randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square

 ePi = i;
 ePj = j;
}


function initThreeEnemyP()
{
 var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 theenemyP = new THREE.Mesh( shape );
 theenemyP.material.color.setHex( BLANKCOLOR  );
 drawEnemyP();
}


function moveLogicalEnemyP(a)
{
// move towards agent
// put some randomness in so it won't get stuck with barriers

 var i = ePi;
 var j = ePj;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;

 if ( ! occupied(i,j) )
 {
  ePi = i;
  ePj = j;
 }
}

// --- enemy FOUR functions -----------------------------------
//BLUE

function drawEnemyO()	// given ei, ej, draw it
{
  var x = translate ( eOi * squaresize );
  var z = translate ( eOj * squaresize );
  var y =  0;

 theenemyO.position.x = x;
 theenemyO.position.y = y;
 theenemyO.position.z = z;
 threeworld.scene.add(theenemyO);

 threeworld.follow.copy ( theenemyO.position );		// if camera moving, look back at where the enemy is
}


function initLogicalEnemyO()
{
//ENEMY START LOCAtion:
 var i, j;
 do
 {
  i =7; //randomintAtoB(1,gridsize-2);
  j =7;// randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square

 eOi = i;
 eOj = j;
}


function initThreeEnemyO()
{
 var shape = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 theenemyO = new THREE.Mesh( shape );
 theenemyO.material.color.setHex( BLANKCOLOR  );
 drawEnemyO();
}


function moveLogicalEnemyO(a)
{
// move towards agent
// put some randomness in so it won't get stuck with barriers

  var i = eOi;
 var j = eOj;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;

 if ( ! occupied(i,j) )
 {
  eOi = i;
  eOj = j;
 }
}


// --- agent functions -----------------------------------


function drawAgent()	// given ai, aj, draw it
{
  var x = translate ( ai * squaresize );
  var z = translate ( aj * squaresize );
  var y =  0;

 theagent.position.x = x;
 theagent.position.y = y;
 theagent.position.z = z;
 threeworld.scene.add(theagent);

 threeworld.lookat.copy ( theagent.position );
 		// follow vector = agent position (for camera following agent)
}


function initLogicalAgent()
{
// start in random location:
 var i, j;
 do
 {
  i =1;// randomintAtoB(1,gridsize-2);
  j = 1; // randomintAtoB(1,gridsize-2);
 }
 while ( occupied(i,j) );  	  // search for empty square

 ai = i;
 aj = j;
 
}

function initThreeAgent()
{
 var shape    = new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
 theagent = new THREE.Mesh( shape );
 theagent.material.color.setHex( BLANKCOLOR );
 drawAgent();
 
}


function moveLogicalAgent( a )			// this is called by the infrastructure that gets action a from the Mind
{
 var i = ai;
 var j = aj;

      if ( a == ACTION_LEFT ) 	i--;
 else if ( a == ACTION_RIGHT ) 	i++;
 else if ( a == ACTION_UP ) 		j++;
 else if ( a == ACTION_DOWN ) 	j--;

 if ( ! occupied(i,j) )
 {
  ai = i;
  aj = j;
 
 }
 
 
}
function eatCircles()
{
             if(GRID[ai][aj]==GRID_CIRCLE)
             
                  return (GRID[ai][aj] = GRID_BLANK);
             
   
}


function keyHandler(e)
// user control
// Note that this.takeAction(a) is constantly running at same time, redrawing the screen.
{
    if (e.keyCode == 37)  moveLogicalAgent ( ACTION_LEFT 	);
    if (e.keyCode == 38)  moveLogicalAgent ( ACTION_DOWN  	);
    if (e.keyCode == 39)  moveLogicalAgent ( ACTION_RIGHT 	);
    if (e.keyCode == 40)  moveLogicalAgent ( ACTION_UP	);
}




// --- score: -----------------------------------

//function badstep()			// is the enemy within one square of the agent
//{
 //if ( ( Math.abs(ei - ai) < 2 ) && ( Math.abs(ej - aj) < 2 ) ) return true;
 //else return false;
//}


function agentBlocked()			// agent is blocked on all sides, run over
{
 return ( 	
        occupied (ai-1,aj)&&
		occupied (ai+1,aj)&&
		occupied (  ai,aj+1)&&
		occupied (  ai,aj-1)
		);
}


function updateStatusBefore(a)
// this is called before anyone has moved on this step, agent has just proposed an action
// update status to show old state and proposed move
{
 var x = self.getState();
 //var eat = eatCircles();
 var status = " Step: <b> " + step ;

 $("#user_span3").html( status );
}


///DYINGGGGGGG
function die()
{
    if( (((ei == ai-1)&&(ej == aj)) || 
        ((ei== ai+1)&&(aj == ej)) ||
        ((ai == ei)&&(ej==aj+1)) ||
        ((ai == ei)&&(aj-1 == ej)))
        ||
        
        (((ai-1 == eTi)&&(eTj == aj)) || 
        ((ai+1==eTi)&&(aj==eTj)) ||
        ((ai == eTi)&&(eTj==aj+1)) ||
        ((ai == eTi)&&(aj-1 == eTj)))
        ||
        
        (((ai-1 == ePi)&&(ePj == aj)) || 
        ((ai+1==ePi)&&(aj==ePj)) ||
        ((ai == ePi)&&(ePj==aj+1)) ||
        ((ai == ePi)&&(aj-1 == ePj)))
        ||
        
        (((ai-1 == eOi)&&(eOi == aj)) || 
        ((ai+1 == eOi)&&(aj==eOj)) ||
        ((ai == eOi)&&(eOi==aj+1)) ||
        ((ai == eOi)&&(aj-1 == eOj)))
        )
    {
        return true;
    }
}

function   updateStatusAfter()		// agent and enemy have moved, can calculate score
{
    
 // new state after both have moved
 var y = self.getState();
 //var status = " &nbsp; y = (" + y.toString() + ") <BR> ";
 //$("#user_span4").html( status );

 var score = self.getScore();
 

 var status = "   USE ARROW KEYPAD TO MOVE THE PACMAN"; 

 $("#user_span5").html( status );
}






//--- public functions / interface / API ----------------------------------------------------------


	this.endCondition;			// If set to true, run will end.



this.newRun = function()
{

// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)

  this.endCondition = false;
  badsteps = 0;
  goodsteps = 0;
  step = 0;
 

 // for all runs:

 	initGrid();
	initLogicalWalls();
	initLogicalCircle();
	initLogicalMaze();
	initLogicalAgent();
    eatCircles();
	initLogicalEnemy();
	initLogicalEnemyT();
	initLogicalEnemyP();
	initLogicalEnemyO();
	

 // for graphical runs only:

  if ( true  )
  {
	if ( show3d )
	{
	 BOXHEIGHT = squaresize;
	 threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  );
	}
	else
	{
	 BOXHEIGHT = 1;
	 threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  );
	}
    
	initSkybox();
 	initMusic();

	// Set up objects first:

	initThreeWalls();
	initThreeCircle();
	initThreeMaze();
	initThreeAgent();
	initThreeEnemy();
    initThreeEnemyT();
    initThreeEnemyP();
    initThreeEnemyO();

	// Then paint them with textures - asynchronous load of textures from files.
	// The texture file loads return at some unknown future time in some unknown order.
	// Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return.
	// It is safe to paint objects in random order, but might not be safe to create objects in random order.

	loadTextures();

	document.onkeydown = keyHandler;
  }

};




this.getState = function()
{
 var x = [ ai, aj, ei, ej, eTi, eTj, eOi, eOj, ePi, ePj ];
  return ( x );
};



this.nextStep = function()		 
{
 var a = 4;

  step++;

  if ( true  )
   updateStatusBefore(a);			// show status line before moves
   
  //moveLogicalAgent(a);
  if ( ( step % 7) == 0 )		// slow the enemy down to every nth step
    moveLogicalEnemy(a);
   moveLogicalEnemyT(a);
   moveLogicalEnemyP(a);
   moveLogicalEnemyO(a);


  if ( true  )
  {
   drawAgent();
   
   drawEnemy();
   drawEnemyT();
   drawEnemyP();
   drawEnemyO();

   updateStatusAfter();
   GRID[ai][aj]== GRID_BLANK;
   // show status line after moves
  }

    if( die() )
    {
        this.endCondition = true;
    }

  if ( agentBlocked() )			// if agent blocked in, run over
  {
	this.endCondition = true;			// you score zero as far as database is concerned
  	if ( true  )
  	{
  	 
	 musicPause();
	 soundAlarm();
	}
  }

};



this.endRun = function()
{
 if ( true  )
 {
  musicPause();
  if ( this.endCondition )
    $("#user_span6").html( " &nbsp; <font color=red> <B> You died :(</B> </font>   "  );
  else
    $("#user_span6").html( " &nbsp; <font color=red> <B> Run over. </B> </font>   "  );
 }
};


this.getScore = function()
{
 return ( ( goodsteps / step ) * 100 );
};


}

//---- end of World class -------------------------------------------------------
// --- music and sound effects ----------------------------------------
// credits:
// http://www.dl-sounds.com/royalty-free/defense-line/
// http://soundbible.com/1542-Air-Horn.html
function initMusic()
{
	// put music element in one of the spans
  	var x = "<audio  id=theaudio  src=/uploads/starter/Pacman.mp3   autoplay loop> </audio>" ;
  	$("#user_span1").html( x );
}


function musicPlay()
{
	// jQuery does not seem to parse pause() etc. so find the element the old way:
 	document.getElementById('theaudio').play();
}


function musicPause()
{
 	document.getElementById('theaudio').pause();
}


function soundAlarm()
{
 	var x = "<audio    src=/uploads/starter/air.horn.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}