Code viewer for World: A1-World 1 (Tanmay Potbhare)

// Cloned by AncientBrain@123 on 25 Oct 2021 from World "Complex World" by Starter user 
// Please leave this clone trail here.
 
// ===================================================================================================================
// === Start of tweaker's box ======================================================================================== 
// ===================================================================================================================

// The easiest things to modify are in this box.
// You should be able to change things in this box without being a JavaScript programmer.
// Go ahead and change some of these. What's the worst that could happen?


AB.clockTick       = 100;    

	// Speed of run: Step every n milliseconds. Default 100.
	
AB.maxSteps        = 1000;    

	// Length of run: Maximum length of run in steps. Default 1000.

AB.screenshotStep  = 50;   
  
	// Take screenshot on this step. (All resources should have finished loading.) Default 50.



//---- global constants: -------------------------------------------------------
 
 const show3d = true;						// Switch between 3d and 2d view (both using Three.js) 


 const TEXTURE_WALL 	= '/uploads/tanmay96/1636505255.png' ;
 const TEXTURE_MAZE 	= '/uploads/tanmay96/1636506825.png' ;
 const TEXTURE_AGENT 	= '/uploads/tanmay96/1636505647.png' ;
 const TEXTURE_ENEMY 	= '/uploads/tanmay96/1637076264.png' ;
 const TEXTURE_Arrow    = '/uploads/tanmay96/1636505748.png';
// credits:
// http://commons.wikimedia.org/wiki/File:Old_door_handles.jpg
// 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

 
	const MUSIC_BACK  = '/uploads/starter/Defense.Line.mp3' ;
	const SOUND_ALARM = '/uploads/starter/air.horn.mp3' ;

// credits:
// http://www.dl-sounds.com/royalty-free/defense-line/
// http://soundbible.com/1542-Air-Horn.html 

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

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

const squaresize = 100;					// size of square in pixels

const MAXPOS = gridsize * squaresize;		// length of one side in pixels 
	
const SKYCOLOR 	= 0xddffdd;				// a number, not a string 

 
const startRadiusConst	 	= MAXPOS * .9 ;		// distance from centre to start the camera at
const maxRadiusConst 		= MAXPOS * 20 ;		// maximum distance from camera we will render things  



//--- change ABWorld defaults: -------------------------------

ABHandler.MAXCAMERAPOS 	= maxRadiusConst ;

ABHandler.GROUNDZERO		= !0;						// "ground" exists at altitude zero



//--- skybox: -------------------------------
// skybox is a collection of 6 files 
// x,y,z positive and negative faces have to be in certain order in the array 
// https://threejs.org/docs/#api/en/loaders/CubeTextureLoader 

// mountain skybox, credit:
// http://stemkoski.github.io/Three.js/Skybox.html

 const SKYBOX_ARRAY = [										 
                "/uploads/deadlock/bkg1_back6.png",
                "/uploads/deadlock/bkg1_back6.png",
                "/uploads/deadlock/bkg1_back6.png",
                "/uploads/deadlock/bkg1_back6.png",
                "/uploads/deadlock/bkg1_back6.png",
                "/uploads/deadlock/bkg1_back6.png"
                ];


// space skybox, credit:
// http://en.spaceengine.org/forum/21-514-1
// x,y,z labelled differently

/*
 const SKYBOX_ARRAY = [										 
                "/uploads/starter/sky_pos_z.jpg",
                "/uploads/starter/sky_neg_z.jpg",
                "/uploads/starter/sky_pos_y.jpg",
                "/uploads/starter/sky_neg_y.jpg",
                "/uploads/starter/sky_pos_x.jpg",
                "/uploads/starter/sky_neg_x.jpg"
                ];
*/				


// urban photographic skyboxes, credit:
// http://opengameart.org/content/urban-skyboxes

/*
 const SKYBOX_ARRAY = [										 
                "/uploads/starter/posx.jpg",
                "/uploads/starter/negx.jpg",
                "/uploads/starter/posy.jpg",
                "/uploads/starter/negy.jpg",
                "/uploads/starter/posz.jpg",
                "/uploads/starter/negz.jpg"
                ];
*/



// ===================================================================================================================
// === End of tweaker's box ==========================================================================================
// ===================================================================================================================


// You will need to be some sort of JavaScript programmer to change things below the tweaker's box.









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

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 theagent, theenemy, shape, thecube, thestar;
  
var wall_texture, agent_texture, enemy_texture, maze_texture, arrow_texture; 


// enemy and agent position on squares
var ei, ej, ai, aj;
var badsteps;
var goodsteps;
var start;
var end;
var xi = [], xj = [], starArray = [], openSet = [], closedSet = [], path = [];


	
function loadResources()		// asynchronous file loads - call initScene() when all finished 
{
	var loader1 = new THREE.TextureLoader();
	var loader2 = new THREE.TextureLoader();
	var loader3 = new THREE.TextureLoader();
	var loader4 = new THREE.TextureLoader();
	var loader5 = new THREE.TextureLoader();
	
	loader1.load ( TEXTURE_WALL, function ( thetexture )  		
	{
		thetexture.minFilter  = THREE.LinearFilter;
		wall_texture = thetexture;
		if ( asynchFinished() )	initScene();		// if all file loads have returned 
	});
		
	loader2.load ( TEXTURE_AGENT, function ( thetexture )  	 
	{
		thetexture.minFilter  = THREE.LinearFilter;
		agent_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});	
	
	loader3.load ( TEXTURE_ENEMY, function ( thetexture )  
	{
		thetexture.minFilter  = THREE.LinearFilter;
		enemy_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});
	
	loader4.load ( TEXTURE_MAZE, function ( thetexture )  
	{
		thetexture.minFilter  = THREE.LinearFilter;
		maze_texture = thetexture;
		if ( asynchFinished() )	initScene();		 
	});
	
	loader5.load ( TEXTURE_Arrow, function ( thetexture)
	{
	    thetexture.minFilter = THREE.LinearFilter;
	    arrow_texture = thetexture;
	    if ( asynchFinished() ) initScene();
	});
	
}


function asynchFinished()		 // all file loads returned 
{
	if ( wall_texture && agent_texture && enemy_texture && maze_texture && arrow_texture)   return true; 
	else return false;
}	
	
	
 

//--- grid system -------------------------------------------------------------------------------
// my numbering is 0 to gridsize-1

	
function occupied(e, t) {
    return ei == e && ej == t || (ai == e && aj == t || !!GRID[e][t].wall)
}

 
// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
// 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 ( e, t )			
{
	var v = new THREE.Vector3();
	
	v.y = 0;	
	v.x = ( e * squaresize ) - ( MAXPOS/2 );   		 
	v.z = ( t * squaresize ) - ( MAXPOS/2 );   	
	
	return v;
}

function heuristic(e, t)
{
    if (t !== undefined)
    {
        return Math.abs( e.i - t.i ) + Math.abs( e.j - t.j);
    }
        
}

function removeFromArray(e, t)
{
    for (var a = e.length - 1; a >= 0; a--) e[a] == t && e.splice(a, 1)
}

function Spot(e, t)
{
    this.i = e, this.j = t, this.f = 0, this.g = 0, this.h = 0, this.neighbors = [], this.previous = void 0, 0 === e || e === gridsize - 1 || 0 === t || t === gridsize - 1 ? (this.wall = !0, shape = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize), (thecube = new THREE.Mesh(shape)).material = new THREE.MeshBasicMaterial({
        map: wall_texture}), 
        thecube.position.copy(translate(e, t)), ABWorld.scene.add(thecube)) : this.wall = !1;
    for (var a = 0; a <= NOBOXES; a++) e === xi[a] && t === xj[a] && (this.wall = !0);
    this.addNeighbors = function(e) {
        var t = this.i,
            a = this.j;
        t < gridsize - 1 && this.neighbors.push(e[t + 1][a]), t > 0 && this.neighbors.push(e[t - 1][a]), a < gridsize - 1 && this.neighbors.push(e[t][a + 1]), a > 0 && this.neighbors.push(e[t][a - 1])

    };    
}



	
function initScene()		// all file loads have returned 
{
    var e, t;
    for (e = 0; e < gridsize; e++) 
        GRID[e] = new Array(gridsize);
        
    for (var a = 0, r = 1; r <= NOBOXES; r++) 
        e = AB.randomIntAtoB(1, gridsize - 2), 
        t = AB.randomIntAtoB(1, gridsize - 2), 
        xi[a] = e, xj[a] = t, 
        shape = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize), 
        (thecube = new THREE.Mesh(shape)).material = new THREE.MeshBasicMaterial({map: maze_texture}), 
        thecube.position.copy(translate(e, t)),
        ABWorld.scene.add(thecube), 
        a++;
        
    for (e = 0; e < gridsize; e++)
        for (t = 0; t < gridsize; t++) 
            GRID[e][t] = new Spot(e, t);
            
    for (e = 0; e < gridsize; e++)
        for (t = 0; t < gridsize; t++) 
            GRID[e][t].addNeighbors(GRID);
            
    do {
        e = AB.randomIntAtoB(1, gridsize - 2), 
        t = AB.randomIntAtoB(1, gridsize - 2)
    } while (occupied(e, t));
    
    ei = e, 
    ej = t, 
    shape = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize), 
    (theenemy = new THREE.Mesh(shape)).material = new THREE.MeshBasicMaterial({map: enemy_texture}), 
    ABWorld.scene.add(theenemy), 
    drawEnemy();
    
    do {
        e = AB.randomIntAtoB(1, gridsize - 2),
        t = AB.randomIntAtoB(1, gridsize - 2)
    } while (occupied(e, t));
    
    ai = e, 
    aj = t, 
    shape = new THREE.BoxGeometry(squaresize, BOXHEIGHT, squaresize), 
    (theagent = new THREE.Mesh(shape)).material = new THREE.MeshBasicMaterial({map: agent_texture}), 
    ABWorld.scene.add(theagent), 
    drawAgent(), 
    ABWorld.scene.background = (new THREE.CubeTextureLoader).load(SKYBOX_ARRAY, function() { ABWorld.render(), AB.removeLoading(), AB.runReady = !0
    })
}    
/*	 var i,j, shape, thecube;
	 
	// set up GRID as 2D array
	 
	 for ( i = 0; i < gridsize ; i++ ) 
		GRID[i] = new Array(gridsize);		 


	// set up walls
	 
	 for ( i = 0; i < gridsize ; i++ ) 
	  for ( j = 0; j < gridsize ; j++ ) 
		if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) )
		{
			GRID[i][j] = GRID_WALL;		 
			shape    = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );			 
			thecube  = new THREE.Mesh( shape );
			thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } );
			
			thecube.position.copy ( translate(i,j) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
			ABWorld.scene.add(thecube);
		}
		else 
   			GRID[i][j] = GRID_BLANK;

		
   // set up maze 
   
    for ( var c=1 ; c <= NOBOXES ; c++ )
	{
		i = AB.randomIntAtoB(1,gridsize-2);		// inner squares are 1 to gridsize-2
		j = AB.randomIntAtoB(1,gridsize-2);
			
		GRID[i][j] = GRID_MAZE ;
		
		shape    = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );			 
		thecube  = new THREE.Mesh( shape );
		thecube.material = new THREE.MeshBasicMaterial( { map: maze_texture } );		  

		thecube.position.copy ( translate(i,j) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
		ABWorld.scene.add(thecube);		
	}
	 	 
   
	// set up enemy 
	// start in random location
	
	 do
	 {
	  i = AB.randomIntAtoB(1,gridsize-2);
	  j = AB.randomIntAtoB(1,gridsize-2);
	 }
	 while ( occupied(i,j) );  	  // search for empty square 

	 ei = i;
	 ej = j;
	 
	 shape    = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );			 
	 theenemy = new THREE.Mesh( shape );
 	 theenemy.material =  new THREE.MeshBasicMaterial( { map: enemy_texture } );
	 ABWorld.scene.add(theenemy);
	 drawEnemy();		  

	 
	
	// set up agent 
	// start in random location
	
	 do
	 {
	  i = AB.randomIntAtoB(1,gridsize-2);
	  j = AB.randomIntAtoB(1,gridsize-2);
	 }
	 while ( occupied(i,j) );  	  // search for empty square 

	 ai = i;
	 aj = j;
 
	 shape    = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );			 
	 theagent = new THREE.Mesh( shape );
	 theagent.material =  new THREE.MeshBasicMaterial( { map: agent_texture } );
	 ABWorld.scene.add(theagent);
	 drawAgent(); 


  // finally skybox 
  // setting up skybox is simple 
  // just pass it array of 6 URLs and it does the asych load 
  
  	 ABWorld.scene.background = new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY, 	function() 
	 { 
		ABWorld.render(); 
	 
		AB.removeLoading();
	
		AB.runReady = true; 		// start the run loop
	 });
 		
}*/
 
 
 


// --- draw moving objects -----------------------------------


function drawEnemy()		// given ei, ej, draw it 
{
	theenemy.position.copy ( translate(ei,ej) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 

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


function drawAgent()		// given ai, aj, draw it 
{
	theagent.position.copy ( translate(ai,aj) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 

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


// ------- to draw a path from enemy to agent while moving towards agent---------

function drawPath() {
    for (var e = 0; e < path.length - 2; e++)
        if (path.length > 2) {
            var t = path[e];
            shape = new THREE.BoxGeometry(squaresize, .1, squaresize), 
            (thestar = new THREE.Mesh(shape)).material = new THREE.MeshBasicMaterial({map: arrow_texture}), 
            starArray[e] = thestar.uuid, 
            thestar.position.copy(translate(t.i, t.j)), 
            ABWorld.scene.add(thestar)
        }
}




// ------- A* Algorithm to trap the agent--------------

function AStar() {
    for (openSet = [], closedSet = [], path = [], start = GRID[ei][ej], end = GRID[ai][aj], openSet.push(start); openSet.length > 0;) {
        for (var e = 0, t = 0; t < openSet.length; t++) 
            openSet[t].f < openSet[e].f && (e = t);
        var a = openSet[e];
        if (a === end) {
            console.log("success - found path");
            break;
        }
        removeFromArray(openSet, a), closedSet.push(a);
        var r = a.neighbors;
        for (t = 0; t < r.length; t++) {
            var n = r[t];
            if (!closedSet.includes(n) && !n.wall) {
                var o = a.g + heuristic(n, a),
                    i = !1;
                //openSet.includes(n) ? o < n.g && (n.g = o, i = !0) : (n.g = o, i = !0, openSet.push(n)), i && (n.h = heuristic(n, end), n.f = n.g + n.h, n.previous = a)
                openSet.includes(n) ? o < n.g && (n.g = o, i = !0) : (n.g = o, i = !0, openSet.push(n)), i && (n.h = heuristic(n, end), n.f = n.g + n.h, n.previous = a)
                
            }
        }
        var s = a;
        for ((path = []).push(s); s.previous;) 
            path.push(s.previous), s = s.previous;
    }
    for (t = 0; t < gridsize; t++)
        for (var u = 0; u < gridsize; u++) 0 !== GRID[t][u].f && (GRID[t][u].f = 0, GRID[t][u].g = 0, GRID[t][u].h = 0, GRID[t][u].previous = void 0)
}



// ------- to remove the node which has been over----------

function removePath() {
    console.log("removing");
    for (var e = 0; e < starArray.length; e++) {
        var t = starArray[e];
        const a = ABWorld.scene.getObjectByProperty("uuid", t);
        ABWorld.scene.remove(a)
    }
}

// --- take actions -----------------------------------

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

 AStar(), drawPath();
    
    var e = path.length,
        t = path[e - 2];
        
    if (t !== undefined){
    occupied(t.i, t.j) || (ei = t.i, ej = t.j);
    }
    
}


function sleep(e) {
    return new Promise(t => setTimeout(t, e))
}


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




// --- key handling --------------------------------------------------------------------------------------
// This is hard to see while the Mind is also moving the agent:
// AB.mind.getAction() and AB.world.takeAction() are constantly running in a loop at the same time 
// have to turn off Mind actions to really see user key control 

// we will handle these keys: 

var OURKEYS = [ 37, 38, 39, 40 ];

function ourKeys ( event ) { return ( OURKEYS.includes ( event.keyCode ) ); }
	

function keyHandler ( event )		
{
	if ( ! AB.runReady ) return true; 		// not ready yet 

   // if not one of our special keys, send it to default key handling:
	
	if ( ! ourKeys ( event ) ) return true;
	
	// else handle key and prevent default handling:
	
	if ( event.keyCode == 37 )   moveLogicalAgent ( ACTION_LEFT 	);   
    if ( event.keyCode == 38 )   moveLogicalAgent ( ACTION_DOWN  	); 	 
    if ( event.keyCode == 39 )   moveLogicalAgent ( ACTION_RIGHT 	); 	 
    if ( event.keyCode == 40 )   moveLogicalAgent ( ACTION_UP		);   
	
	// when the World is embedded in an iframe in a page, we want arrow key events handled by World and not passed up to parent 

	event.stopPropagation(); event.preventDefault(); return false;
}





// --- 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(e) {
    AB.world.getState();
    AB.msg(" Step: " + AB.step + " &nbsp; a = (" + e + ") ")
}


function updateStatusAfter() {
    AB.world.getState();
    var e = goodsteps / AB.step * 100;
    AB.msg(" Bad steps: " + badsteps + " &nbsp; Good steps: " + goodsteps + " &nbsp; Score: " + e.toFixed(2) + "% ", 2)
}
AB.world.newRun = function() {
    AB.loadingScreen(), AB.runReady = !1, badsteps = 0, goodsteps = 0, BOXHEIGHT = squaresize, ABWorld.init3d(startRadiusConst, maxRadiusConst, 14548957), loadResources(), document.onkeydown = keyHandler
}, AB.world.getState = function() {
    return [ai, aj, ei, ej, GRID]
}, AB.world.takeAction = function(e) {
    updateStatusBefore(e), moveLogicalAgent(e), AB.step % 2 === 0 && moveLogicalEnemy(), badstep() ? badsteps++ : goodsteps++, drawAgent(), drawEnemy(), updateStatusAfter(), agentBlocked() && (AB.abortRun = !0, goodsteps = 0, musicPause(), soundAlarm()), sleep(100).then(() => {
        removePath()
    })
}, AB.world.endRun = function() {
    musicPause(), AB.abortRun ? AB.msg(" <br> <font color=red> <B> Agent trapped. Final score zero. </B> </font>   ", 3) : AB.msg(" <br> <font color=green> <B> Run over. </B> </font>   ", 3)
}, AB.world.getScore = function() {
    var e = goodsteps / AB.maxSteps * 100;
    return Math.round(100 * e) / 100
};


 




// --- music and sound effects ----------------------------------------

var backmusic = AB.backgroundMusic ( MUSIC_BACK );

function musicPlay()   { backmusic.play();  }
function musicPause()  { backmusic.pause(); }

											 
function soundAlarm()
{
	var alarm = new Audio ( SOUND_ALARM );
	alarm.play();							// play once, no loop 
}