Code viewer for World: Get Over the Wall (Project...
/* Get over the wall, created by Daniel Holton

This game is a fairly basic concept, to navigate a maze and end to the endzone while avoiding projectiles shot by an enemy.

Due to the recent election of trump, I thought it would be a bit of a laugh to create to create a game where a wee mexican chap had to scale over the proposed wall trump is building.

The map is partitioned into different sections in order to create a basic maze, a random maze is then also created to make navigation more challenging. Unfortunately this sometimes results in no valid path for the mexican chap
to move to the end. I tried to remedy this by allowing a gap of 1 square on the far side of the maze. This did not completely fix the problem but I found that a blockage would only occur every 10 runs or so.

Navigation is made possible by the arrow keys, and the game will terminate whenever we reach the end or we get hit by one of trumps bricks.

*/


const	 	CLOCKTICK 	= 30;					// I sped up the run a bit, this was to make the projectiles move faster. Decreasing this number will result in a more challanging game
const		MAXSTEPS 	= 10000000;					// length of a run before final score
 

const  LIGHTCOLOR 	= 0xffffff ;



//---- global constants: -------------------------------------------------------

const gridsize = 50 ;						// number of squares along side of world	   

const NOBOXES =  Math.trunc ( (gridsize * gridsize)/10 );
		

const squaresize = 30;					
const MAXPOS = gridsize * squaresize;		
	
const SKYCOLOR 	= 0xf4a460;				
const BLANKCOLOR 	= 0x00ff00 ;
const grey = 0x808080;
const red = SKYCOLOR           




const show3d = false;						// Personally, I believe it's far easier to navigate the maze in 2D 
 
const startRadiusConst	 	= MAXPOS * 0.8 ;		
const skyboxConst			= MAXPOS * 3 ;		
const maxRadiusConst 		= MAXPOS * 10  ;		 





//--- 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;


// contents of a grid square

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

// --- 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; }
}







//---- start of World class -------------------------------------------------------
 
function World() { 





var BOXHEIGHT;		


var GRID 	= new Array(gridsize);			 
var WALLS 	= new Array ( 4 * gridsize );		
var MAZE 	= new Array ( NOBOXES);

var theagent, brick, theenemy;
  


var ei, ej, ai, aj, si, sj,bi,bj; // e= enemy coords---- a = agent coords----- b = brick coords

var  step;

var dBrick = false;				//boolean to represent if a brick is thrown or not
var isShot = false;					// boolean to represent if the agent is shot 
var isEnd = false;					// boolean to represent if the agent has reached the end of the maze

 	var self = this;						

 


// regular "function" syntax means private functions:

function initGrid()
{
 for (var i = 0; i < gridsize ; i++) 
 {
  GRID[i] = new Array(gridsize);		// each element is an array 

  for (var j = 0; j < gridsize ; j++) 
  {
   GRID[i][j] = GRID_BLANK ;
  }
 }
}

function occupied ( i, j )		// is this square occupied
{
 if ( ( ei == i +1 ) && ( ej == j+1 ) ) return true;		// variable objects 
 if ( ( ai == i -1 ) && ( aj == j+1 ) ) return true;
 if ( ( si == i +1 ) && ( sj == j-1 ) ) return true;

 if ( GRID[i][j] == GRID_WALL) return true;	
 if ( GRID[i][j] == GRID_MAZE) return true;// fixed objects	 	 
	 
 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/james/winterjh6fixed.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh2.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh5.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/holtond2/border.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh4.jpg" ), side: THREE.BackSide } ) ),
 	( new THREE.MeshBasicMaterial ( { map: THREE.ImageUtils.loadTexture( "/uploads/james/winterjh1.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
}

//These are the brick textures, the program wouldn't draw the brick texture if it was called at the same time and place as the other textures
function loadtextures2()
{
    var loader4 = new THREE.TextureLoader();
 loader4.load ( '/uploads/holtond2/projectile.jpg',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		brick.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} ); 
}

function loadTextures()
{

 var loader1 = new THREE.TextureLoader();
 loader1.load ( '/uploads/holtond2/brick.jpg',		function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
	} ); 

 var loader2 = new THREE.TextureLoader();
 loader2.load ( '/uploads/holtond2/brick.jpg',		function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		paintMaze ( new THREE.MeshBasicMaterial( { map: thetexture } ) );
 	} ); 

 var loader3 = new THREE.TextureLoader();
 loader3.load ( '/uploads/holtond2/mehico.png',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		theagent.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} ); 
	var loader5 = new THREE.TextureLoader();
 loader5.load ( '/uploads/holtond2/trump.jpg',	function ( thetexture ) {			 
		thetexture.minFilter = THREE.LinearFilter;
		theenemy.material =  new THREE.MeshBasicMaterial( { map: thetexture } );
	} );

}

// --- add fixed objects ---------------------------------------- 
   
 
 
function initLogicalWalls()			
{
 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 ;		 
   }
}

//These are all the mazes that have been drawn in the program
function initRightMaze()
{
	for(var i = 3; i <gridsize-1 ;i++)
		for(var j = 0; j < gridsize-1 ; j++)
		{
			j = j+10;
			if((i != 0) || (i != gridsize-1) || (j != 0) || (j != gridsize -1))
			{
				GRID[i][j] = GRID_MAZE;
			}
		}
}
function initLeftMaze()
{
	for(var i = 1; i <gridsize-4 ;i++)
		for(var j = 6; j < gridsize-1 ; j++)
		{
			j = j+10;
			if((i != 0) || (i != gridsize-1) || (j != 0) || (j != gridsize -1))
			{
				GRID[i][j] = GRID_MAZE;
			}
		}
}
function initShooterPartition()
{
	
	for(var i = 1; i < gridsize-1; i++)
	{
		GRID[i][47] = GRID_MAZE;
	}
}

function initLogicalMaze()		 
{
 for ( var c=1 ; c <= NOBOXES ; c++ )
 {
  	var i = randomintAtoB(2,gridsize-4);	
  	var j = randomintAtoB(1,gridsize-3);
    	GRID[i][j] = GRID_MAZE ;		 
 }
}
function initThreeWalls()		
{
 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  );			  
 
    	 thecube.position.x = translate ( i * squaresize );   		 
    	 thecube.position.z = translate ( j * squaresize );   	
    	 thecube.position.y =  0;	
 
 	 threeworld.scene.add(thecube);
	 WALLS[t] = thecube;				
	 t++; 
   }
}


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


function initThreeMaze()		  	
{
 var t = 0;
 for (var i = 0; i < gridsize ; i++) 
  for (var j = 0; j < gridsize ; j++) 
   if ( GRID[i][j] == 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 ( i * squaresize );   	
  	thecube.position.z = translate ( j * squaresize );   	
  	thecube.position.y =  0;	
 
 	threeworld.scene.add(thecube);
	MAZE[t] = thecube;		
	t++; 
   }
}


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



// --- brick functions -----------------------------------




function drawBrick()	 
{
  var x = translate ( bi * squaresize );   	
  var z = translate ( bj * squaresize );   	
  var y =  0;	

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

 threeworld.lookat.copy ( brick.position );		
}



function initLogicalBrick()
{
 var i, j;
 do
 {
  i = ei;
  j = ej;
 }
 while ( occupied(i,j) ); 

 bi = i;
 bj = j;
}

function initThreeBrick()
{
 var shape    = new THREE.BoxGeometry( 15, 15, 15 );			 
 brick = new THREE.Mesh( shape );
 brick.material.color.setHex( BLANKCOLOR  );	
 drawBrick();		  
}


function moveLogicalBrick()
{ 
 var i, j;
	if(bj != 0) //The bullet keeps moving until it reaches the end of the course
	{
		i = bi; 
		j = bj -1; //The bullet only has to move through the j-axis
		bi = i;
		bj = j;
	}
}


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

function drawAgent()	
{
if ( theagent )
{
  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.follow.copy ( theagent.position );		

}
}



function initLogicalAgent()
{
 ai = 48;
 aj = 2;
}

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

 drawAgent(); 		  
}


function moveLogicalAgent( a )			
{ 
 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;
 }
}




// ------Enemy functions -----------------------------------------

function drawEnemy()	
{
  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.lookat.copy ( theenemy.position );		
}


function initLogicalEnemy()
{
 ei = 48;
 ej = 48;
}


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


function moveLogicalEnemy()
{ 


 var i, j;
 if ( ei < ai ) i = randomintAtoB(ei, ei+1); 
 if ( ei == ai ) i = ei; 
 if ( ei > ai ) i = randomintAtoB(ei-1, ei); 

 if ( ej < aj ) j = randomintAtoB(ej, ej+1); 
 if ( ej == aj ) j = ej; 
 if ( ej > aj ) j = randomintAtoB(ej-1, ej); 
 
 if ( ! occupied(i,j) )  	
 {
  ei = i;
  ej = j;
 }
}



function keyHandler(e)		
{
    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	);
}


//----- Checks if the user has been shot --------------------------------
function gotShot()
{
	if(bi == ai && bj == aj)
	{
		isShot = true;
	}
}
//------- Checks if the user has reached the end of the maze ---------
function gotEnd()
{
	if(ai == 46 &&(aj == 46 || aj == 45 || aj == 44))
	{
		isEnd = true;
	}
}

// --- 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 count()
{
  var x = x + 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 status = " Step: <b> " + step + " </b> &nbsp; x = (" + x.toString() + ") &nbsp; a = (" + a + ") "; 

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


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 = "   Bad steps: " + badsteps + 
		" &nbsp; Good steps: " + goodsteps + 
		" &nbsp; Score: " + score.toFixed(2) + "% "; 

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





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


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



this.newRun = function() 
{
  this.endCondition = false;
  badsteps = 0;	
  goodsteps = 0;
	step = 0;


 // for all runs:
	initGrid();
	initLogicalWalls(); 
	initRightMaze();
	initLeftMaze();
	initShooterPartition();
	initLogicalMaze();
	initLogicalAgent();
	initLogicalEnemy();
	
 // for graphical runs only:

  if ( true  )
  {
     // var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
	  //	 thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
	   	// threeworld.scene.add(thelight);
	if ( show3d )
	{
	 BOXHEIGHT = squaresize;
	 threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR  );
	 var thelight = new THREE.DirectionalLight ( LIGHTCOLOR, 3 );
	  //	 thelight.position.set ( startRadiusConst, startRadiusConst, startRadiusConst );
	   	 threeworld.scene.add(thelight);
	 
	}	     
	else
	{
	 BOXHEIGHT = 1;
	 threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 		     
	}

	initSkybox();
 	initMusic();

	// Set up objects first:

	initThreeWalls();
	initThreeMaze();
	initThreeAgent();
	initThreeEnemy();

	// 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, si ,sj ];
  return ( x );  
};
 


this.takeAction = function ( a )
{
  step++;

  if ( true  )
   updateStatusBefore(a);			// show status line before moves 
	
	moveLogicalEnemy();
	moveLogicalEnemy();
	if(step % 50 ==0)
	{
		dBrick = true;
		initThreeBrick();
		initLogicalBrick(); 
		loadtextures2();
	}
        moveLogicalBrick();
		gotShot();
		gotEnd();
		
	
  if ( badstep() )
   badsteps++;
  else
   goodsteps++;

  if ( true  )
  {
   drawAgent();
   drawEnemy();

   if (dBrick == true)
	drawBrick();
 
	if(isEnd == true)
	{
		this.endCondition = true;
		musicPause();
		mexicanHat();
	}
	if(isShot == true)
	{
		this.endCondition = true;
		musicPause();
		sadMusic();
	}
   updateStatusAfter();			// show status line after moves  
  }

};



this.endRun = function()
{
 if ( true  )
 {
  
  if ( this.endCondition )
  {
	if(isShot == true)
		$("#user_span6").html( " &nbsp; <font color=red> <B>You've been BRICKED</B> </font>   "  );
	if(isEnd == true)
		$("#user_span6").html( " &nbsp; <font color=red> <B> You Made it over the Wall, Congratulations! </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/holtond2/stupidquote-donaldtrumpremixofficialmusicvideo.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 sadMusic()
{
 	var x = "<audio    src=/uploads/holtond2/sadpianoitshardtosaygoodbyebymichaelortegamp3cut.net.mp3   autoplay  > </audio>";
  	$("#user_span2").html( x );
}
function mexicanHat()
{
 	var x = "<audio    src=/uploads/holtond2/mexicanhat.mp3   autoplay  > </audio>";
  	$("#user_span3").html( x );
}