Code viewer for World: A* trial

// Cloned by Paul Jones on 9 Nov 2022 from World "Enemy versus Agent Game (clone by Aoife Doherty)" by Aoife Doherty 
// Please leave this clone trail here.
 
AB.clockTick=100;

AB.maxSteps=1000;

AB.screenshotStep=50;

const show3d=false;

TEXTURE_WALL="/uploads/starter/door.jpg";
TEXTURE_MAZE="/uploads/paul6991/wall.jpg";

TEXTURE_AGENT="/uploads/paul6991/joker.jpg";
TEXTURE_ENEMY="/uploads/paul6991/batman.jpg";

// MUSIC_BACK="/uploads/paul6991/james_bond_theme.mp3";
SOUND_ALARM="";

gridsize=30;

NOBOXES = Math.trunc(gridsize*gridsize/5);

squaresize=100;

spheresize=55;

MAXPOS=gridsize*squaresize;

SKYCOLOR=14548957;

startRadiusConst=.8*MAXPOS;

maxRadiusConst=10*MAXPOS;

ABHandler.MAXCAMERAPOS=maxRadiusConst;

ABHandler.GROUNDZERO=!0;

const SKYBOX_ARRAY=["/uploads/aoifemariedoherty/bluecloud_rt.jpg",
"/uploads/aoifemariedoherty/bluecloud_lf.jpg",
"/uploads/aoifemariedoherty/bluecloud_up.jpg",
"/uploads/aoifemariedoherty/bluecloud_dn.jpg",
"/uploads/aoifemariedoherty/bluecloud_bk.jpg",
"/uploads/aoifemariedoherty/bluecloud_ft.jpg"];

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

GRID_BLANK=0;
GRID_WALL=1
GRID_MAZE=2;

var BOXHEIGHT;

var GRID=new Array(gridsize);

var theagent,theenemy;

var wall_texture,agent_texture,enemy_texture,maze_texture;

var ei,ej,ai,aj;

var badsteps,goodsteps;

var shownPath=[],path=[]; //for A*

function loadResources()
{
    var e=new THREE.TextureLoader();
    var a=new THREE.TextureLoader();
    var t=new THREE.TextureLoader();
    var i=new THREE.TextureLoader();
    
    e.load(TEXTURE_WALL,function(e)
    {
        e.minFilter=THREE.LinearFilter;
        wall_texture=e;
        asynchFinished()&&initScene();
    });
    
    a.load(TEXTURE_AGENT,function(e)
    {
        e.minFilter=THREE.LinearFilter;
        agent_texture=e;
        asynchFinished()&&initScene();
    });
    
    t.load(TEXTURE_ENEMY,function(e)
    {
        e.minFilter=THREE.LinearFilter;
        enemy_texture=e;
        asynchFinished()&&initScene();
    });
    
    i.load(TEXTURE_MAZE,function(e)
    {
        e.minFilter=THREE.LinearFilter;
        maze_texture=e;
        asynchFinished()&&initScene();
    });
}
    
    
function asynchFinished()
{
    if ( wall_texture && agent_texture && enemy_texture && maze_texture )   return true; 
	else return false;
}
    
    
function occupied(e,a)
{
    return ei==e&&ej==a||(ai==e&&aj==a||(GRID[e][a]==GRID_WALL||GRID[e][a]==GRID_MAZE))
    
    // ==============for better understanding of code=============
    //  if ( ( ei == e ) && ( ej == a ) ) return true;		// variable objects 
    //  if ( ( ai == e ) && ( aj == a ) ) return true;

    //  if ( GRID[e][a] == GRID_WALL ) return true;		// fixed objects	 
    //  if ( GRID[e][a] == GRID_MAZE ) return true;		 
	 
    //  return false;
}

function translate(e,a)
{
    var t=new THREE.Vector3;
    return t.y=0,t.x=e*squaresize-MAXPOS/2,t.z=a*squaresize-MAXPOS/2,t
    
// ==============for better understanding of code=============
//     var v = new THREE.Vector3();
	
// 	v.y = 0;	
// 	v.x = ( e * squaresize ) - ( MAXPOS/2 );   		 
// 	v.z = ( a * squaresize ) - ( MAXPOS/2 );   	
	
// 	return v;
    
}

function initScene()
{
    var e,a;
    var t; //shape
    var i; //thecube
    
    // set up GRID as 2D array
    for(e=0;e<gridsize;e++)
    {
        GRID[e]=new Array(gridsize);
    }
    
    //set up walls
    
    for(e=0;e<gridsize;e++){
        for(a=0;a<gridsize;a++){
            if (0==e||e==gridsize-1||0==a||a==gridsize-1)
            {
                GRID[e][a]=GRID_WALL;
                t=new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize);
                i=new THREE.Mesh(t);
                i.material=new THREE.MeshBasicMaterial({map:wall_texture});
                
                i.position.copy(translate(e,a));
                ABWorld.scene.add(i);
                
            }
            else
                GRID[e][a]=GRID_BLANK;
            
        }
        
    }
    
    //set up maze  
                
    for(var r=1;r<=NOBOXES;r++)
    {
        e=AB.randomIntAtoB(1,gridsize-2);
        a=AB.randomIntAtoB(1,gridsize-2);
        
        GRID[e][a]=GRID_MAZE;
        
        t=new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize);
        i=new THREE.Mesh(t);
        i.material=new THREE.MeshBasicMaterial({map:maze_texture});
        
        i.position.copy(translate(e,a));
        ABWorld.scene.add(i);
    }
    
    //set up enemy
    do
    {
        e=AB.randomIntAtoB(1,gridsize-2);
        a=AB.randomIntAtoB(1,gridsize-2);
    }
    
    while(occupied(e,a));
    
    ei=e;
    ej=a;
    
    t=new THREE.SphereGeometry(spheresize,BOXHEIGHT,spheresize);
    theenemy=new THREE.Mesh(t);
    theenemy.material=new THREE.MeshBasicMaterial({map:enemy_texture});
    ABWorld.scene.add(theenemy);
    drawEnemy();
    
    //set up agent
    do
    {
        e=AB.randomIntAtoB(1,gridsize-2);
        a=AB.randomIntAtoB(1,gridsize-2);
    }
    
    while(occupied(e,a));
    
    ai=e,
    aj=a,
    t=new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize);
    theagent=new THREE.Mesh(t);
    theagent.material=new THREE.MeshBasicMaterial({map:agent_texture});
    ABWorld.scene.add(theagent);
    drawAgent();
    
    
    //finally skybox
    ABWorld.scene.background=new THREE.CubeTextureLoader().load(SKYBOX_ARRAY,function()
    {
        ABWorld.render();
        AB.removeLoading();
        AB.runReady=!0
    })
}


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

function drawEnemy()
{
    theenemy.position.copy(translate(ei,ej));
    ABWorld.lookat.copy(theenemy.position)
}

function drawAgent()
{
    theagent.position.copy(translate(ai,aj));
    ABWorld.follow.copy(theagent.position)
}

    // Function to delete element from the array
function removeFromArray(e,a)
{
    for(var t=e.length-1;t>=0;t--)  // Could use indexOf here instead to be more efficient
        if (e[t] == a)
            e.splice(t,1)
}
    
    
function MakeSpot(e,a)
{
    // Location
    this.i=e;
    this.j=a;
    
    // f, g, and h values for A*
    this.f=0;
    this.g=0;
    this.h=0;
    this.neighbors=[];
    this.previous=undefined;
    if (GRID[e][a]!== GRID_BLANK)
        this.wall=GRID[e][a]
    //this.wall=GRID[e][a]!==GRID_BLANK;
    
    this.addNeighbors=function(e)
    {
        // To figure out who are the neighbors
        var a=this.i;
        var t=this.j;
     
    //   for better understanding
    if (a < gridsize - 1)   this.neighbors.push(e[a + 1][t]);
    if (a > 0)          this.neighbors.push(e[a - 1][t]);
    if (t < gridsize - 1)   this.neighbors.push(e[a][t + 1]);
    if (t > 0)          this.neighbors.push(e[a][t - 1]);
    }
}


function heuristic(e,a)
{
    return Math.abs(e.i-a.i)+Math.abs(e.j-a.j)
}


//fucntion to show path chosen

function highlightPath(e)
{
    for(var i=0;i<e.length-1;i++)
    {
        shape=new THREE.BoxGeometry(squaresize,.1,squaresize);
        ShownPath=new THREE.Mesh(shape);
        ShownPath.material=new THREE.MeshBasicMaterial({color:'palegreen'});
        ShownPath.position.copy(translate(e[i].i,e[i].j));
        ABWorld.scene.add(ShownPath);
        shownPath.push(ShownPath);
    }
}


var k=[] //testing variable
function moveLogicalEnemy()
{
    var e=new Array(gridsize);
    var a=[];
    var t=[];
    
    for(i=0;i<shownPath.length;i++)
        ABWorld.scene.remove(shownPath[i]);
        
    for(i=0;i<gridsize;i++)
        e[i]=new Array(gridsize);
    
    for(i=0;i<gridsize;i++)
        for(var r=0;r<gridsize;r++)
            e[i][r]=new MakeSpot(i,r);
            
    for(i=0;i<gridsize;i++)
        for(r=0;r<gridsize;r++)
            e[i][r].addNeighbors(e);
            
    
    for(a.push(e[ei][ej]);a.length>0;)
    {
        var o=0;
        for(i=0;i<a.length;i++)
            if (a[i].f<a[o].f)
                o=i;
            var n=a[o];
            
            if(n===e[ai][aj])
            {
                var s=[],d=n;
                for (s.push(d);d.previous;)
                    s.push(d.previous),d=d.previous;
                highlightPath(s);
                var u=s.length-2;
                occupied(s[u].i,s[u].j)||(ei=s[u].i,ej=s[u].j);
                break;
            }
            removeFromArray(a,n);
            t.push(n);
            var c=n.neighbors;
            for(i=0;i<c.length;i++)
            {
                var l=c[i];
                if(l[i]===e[ai][aj])
                {
                    l.previous=n;
                    break;
                }
                // if(!t.includes(l) && !l.wall)
                //     {
                //         var h=n.g + heuristic(l,n) ;
                //           p=false;
                //         if (a.includes(l))
                //             {
                //                 if (h<l.g)
                //                 {
                //                     l.g=h;
                //                     p=true;
                //                 }
                            
                //                 else
                //                 {
                //                     l.g=h;
                //                     p=true;
                //                     a.push(l);
                //                 }}
                            
                //         if (p == true)
                //         {
                //             l.h=heuristic(l,e[ai][aj]);
                //             l.f=l.g+l.h;
                //             l.previous=n;
                //         }
                //     }
                            
                        
                    
                if(!t.includes(l) && !l.wall)
                    {
                        var h=n.g+heuristic(l,n);
                        p=false;
                        a.includes(l)?h<l.g&&(l.g=h,p=!0):(l.g=h,p=!0,a.push(l)),p&&(l.h=heuristic(l,e[ai][aj]),l.f=l.g+l.h,l.previous=n);
                    }
                        
            
            }
    }
}

    
    
function moveLogicalAgent(e)
{
    var a=ai;
    var t=aj;
    if (e==ACTION_LEFT) a--;
    else if (e==ACTION_RIGHT)   a++;
    else if (e==ACTION_UP)  t++;
    else if (e==ACTION_DOWN)    t--;
    
    if (!occupied(a,t))
    {
        ai=a;
        aj=t;
    }
}
     
    
    
// --- 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(e) { return (OURKEYS.includes(e.keyCode) )}
    
    function keyHandler(e)
     {
    //     return!AB.runReady||(!ourKeys(e)||(37==e.keyCode&&moveLogicalAgent(ACTION_LEFT),38==e.keyCode&&moveLogicalAgent(ACTION_DOWN),39==e.keyCode&&moveLogicalAgent(ACTION_RIGHT),40==e.keyCode&&moveLogicalAgent(ACTION_UP);
    //     e.stopPropagation(),e.preventDefault(),!1))}
        if ( ! AB.runReady ) return true; 		// not ready yet 

   // if not one of our special keys, send it to default key handling:
	
	    if ( ! ourKeys ( e ) ) return true;
	
	// else handle key and prevent default handling:
	
	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		);   
	
	// 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 

	e.stopPropagation(); 
	
	e.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)
// 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 a=AB.world.getState();
    //AB.msg(" Step: "+AB.step+"   x = ("+a.toString()+")   a = ("+e+") ")
    AB.msg ( " Step: " + AB.step + " &nbsp; x = (" + a.toString() + ") &nbsp; a = (" + e + ") " ); 
}
    
    
function updateStatusAfter() // agent and enemy have moved, can calculate score
{
 // new state after both have moved
 
    var e=AB.world.getState();
    var a=goodsteps/AB.step*100;
    
    AB.msg("   y = ("+e.toString()+") <br> Bad steps: "+badsteps+"   Good steps: "+goodsteps+"   Score: "+a.toFixed(2)+"% ",2);
   
}






AB.world.newRun=function()
{
    AB.loadingScreen();
    AB.runReady=!1;
    badsteps=0;
    goodsteps=0;
    if (show3d)
    {
        BOXHEIGHT=squaresize;
        ABWorld.init3d(startRadiusConst,maxRadiusConst,14548957);
    }
    
    
    else
	{
	 BOXHEIGHT = 1;
	 ABWorld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR  ); 		     
	}
        
        
    loadResources();    // aynch file loads		
						// calls initScene() when it returns 
    
    document.onkeydown=keyHandler
};

AB.world.getState=function()
{
    var x = [ ai, aj, ei, ej ];
  return ( x );
};


AB.world.takeAction=function(e)
{
    updateStatusBefore(e);
    moveLogicalAgent(e);
    if ( AB.step%2==0)
        moveLogicalEnemy();
        
    if ( badstep() ) badsteps++;
    else goodsteps++;
    
    drawAgent();
    drawEnemy();
    updateStatusAfter();
    
    if ( agentBlocked() )
     {AB.abortRun=!0;
     goodsteps=0;
     musicPause();
     soundAlarm()}
};
    


AB.world.endRun=function()
{
    musicPause();
    if ( AB.abortRun ) AB.msg(" <br> <font color=red> <B> Indiana Jones trapped. Final score zero. </B> </font>   ",3);
    else AB.msg(" <br> <font color=green> <B> Run over. </B> </font>   ",3);
};


AB.world.getScore=function()
{
    // only called at end - do not use AB.step because it may have just incremented past AB.maxSteps
    
    var e=goodsteps/AB.maxSteps*100;    // float like 93.4372778 
    var x= Math.round(100*e)/100;
    return x
};


var backmusic=AB.backgroundMusic( "/uploads/paul6991/james_bond_theme.mp3" );

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


function soundAlarm()
{
    var alarm=new Audio( SOUND_ALARM );
    alarm.play();
}