// 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 + " x = (" + a.toString() + ") 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();
}