// Cloned by James O'Boyle on 26 Nov 2018 from World "Cloned CA318 2018 World" by cdaly // Please leave this clone trail here.// Cloned by cdaly on 11 Oct 2018 from World "CA318 2018 World" by test // Please leave this clone trail here.// - Internal maze (randomly drawn each time)// - Enemy actively chases agent// ======================================================================================================// Scoring:// Bad steps = steps where enemy is within one step of agent.// Good steps = steps where enemy is further away. // Score = good steps as percentage of all steps.//// There are situations where agent is trapped and cannot move.// If this happens, you get BLOCKPUNISH bad steps and the problem is reset - enemy is moved to a random square// ======================================================================================================const BLOCKPUNISH =50;// user_span1 - Status // user_span2 - Status // user_span3 - End message //--- 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)// ===================================================================================================================// === Start of tweaker's box ======================================================================================== // ===================================================================================================================// The easiest things to modify are in this box.
AB.clockTick =50;// 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 TEXTURE_WALL ='/uploads/oboylej5/2d698c4816bdcbde0d86c3309c4c9173.png';const TEXTURE_MAZE ='/uploads/oboylej5/32b4765611826e98fbabdd99bd71a7ac.jpg';const TEXTURE_AGENT ='/uploads/oboylej5/315obHCYVWL.jpg';const TEXTURE_ENEMY ='/uploads/oboylej5/391114_looney-tunes-wile-e-coyote-1080x1920-jpg_1080x1920_h.jpg';// 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).jpgconst SOUND_ALARM ='/uploads/starter/air.horn.mp3';// http://soundbible.com/1542-Air-Horn.html const gridsize =20;// number of squares along side of world const BOXDENSITY =0.1;const NOBOXES =Math.trunc ((gridsize * gridsize)* BOXDENSITY );// density of maze - number of internal boxes// (bug) use trunc or can get a non-integer const squaresize =100;// size of square in pixelsconst MAXPOS = gridsize * squaresize;// length of one side in pixels const SKYCOLOR =0xddffdd;// a number, not a string const startRadiusConst = MAXPOS *0.8;// distance from centre to start the camera atconst skyboxConst = MAXPOS *2;// where to put skybox const maxRadiusConst = MAXPOS *10;// maximum distance from camera we will render things //--- change ABWorld defaults: -------------------------------ABHandler.MAXCAMERAPOS = skyboxConst *0.6;// keep camera inside skybox ABHandler.GROUNDZERO =true;// "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 // mountain skybox, credit:// http://stemkoski.github.io/Three.js/Skybox.html/*
const SKYBOX_ARRAY = [
"/uploads/starter/dawnmountain-xpos.png",
"/uploads/starter/dawnmountain-xneg.png",
"/uploads/starter/dawnmountain-ypos.png",
"/uploads/starter/dawnmountain-yneg.png",
"/uploads/starter/dawnmountain-zpos.png",
"/uploads/starter/dawnmountain-zneg.png"
];
*/const SKYBOX_ARRAY =[//"/uploads/starter/st.posx.jpg",//"/uploads/starter/st.negx.jpg",//"/uploads/starter/st.posy.jpg",//"/uploads/starter/st.negy.jpg",//"/uploads/starter/st.posz.jpg",//"/uploads/starter/st.negz.jpg""/uploads/oboylej5/Chalbi_Desert_Panorama.jpg","/uploads/oboylej5/Chalbi_Desert_Panorama.jpg","/uploads/oboylej5/825778252.jpg","/uploads/oboylej5/gettyimages-736054223_wide-2e29b3ffc4a896a2bea091e0aada1f250c9f9aec.jpg","/uploads/oboylej5/Chalbi_Desert_Panorama.jpg","/uploads/oboylej5/Chalbi_Desert_Panorama.jpg"];// ===================================================================================================================// === End of tweaker's box ==========================================================================================// ===================================================================================================================// contents of a grid squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;//---- start of World class -------------------------------------------------------functionWorld(){var BOXHEIGHT = squaresize;var GRID =newArray(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array var theagent, theenemy;var wall_texture, agent_texture, enemy_texture, maze_texture;var skybox_texture_array =newArray(6);// a cube // enemy and agent position on squaresvar ei, ej, ai, aj;var badsteps;var goodsteps;var stuckfor =0;// how long the enemy has been stuck - if stuck for a while start making random moves var self =this;// needed for private fn to call public fn 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();
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();});for(var i =0; i <6; i++)
skyboxFileLoad ( i );// launch 6 more asynchronous file loads for skybox }function skyboxFileLoad ( n )// asynchronous file load of skybox texture n {var loader =new THREE.TextureLoader();
loader.load ( SKYBOX_ARRAY[n],function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
skybox_texture_array[n]= thetexture;if( asynchFinished()) initScene();});}function asynchFinished()// all file loads returned {for(var i =0; i <6; i++)if(! skybox_texture_array[i])returnfalse;// else if( wall_texture && agent_texture && enemy_texture && maze_texture )returntrue;elsereturnfalse;}//--- grid system -------------------------------------------------------------------------------// my numbering is 0 to gridsize-1function occupied ( i, j )// is this square occupied{if(( ei == i )&&( ej == j ))returntrue;// variable objects if(( ai == i )&&( aj == j ))returntrue;if( GRID[i][j]== GRID_WALL )returntrue;// fixed objects if( GRID[i][j]== GRID_MAZE )returntrue;returnfalse;}// 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 ( i, j ){var v =new THREE.Vector3();
v.y =0;
v.x =( i * squaresize )-( MAXPOS/2);
v.z =( j * squaresize )-( MAXPOS/2);return v;}function initScene()// all file loads have returned {var i,j, shape, thecube;// set up GRID as 2D arrayfor( i =0; i < gridsize ; i++)
GRID[i]=newArray(gridsize);// set up wallsfor( 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
resetEnemyPos();
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 locationdo{
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();// set up skybox var skybox_material_array =[(new THREE.MeshBasicMaterial({ map: skybox_texture_array[0], side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: skybox_texture_array[1], side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: skybox_texture_array[2], side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: skybox_texture_array[3], side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: skybox_texture_array[4], side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: skybox_texture_array[5], side: THREE.BackSide}))];var skyGeometry =new THREE.CubeGeometry( skyboxConst, skyboxConst, skyboxConst );var skyMaterial =new THREE.MeshFaceMaterial( skybox_material_array );var theskybox =new THREE.Mesh( skyGeometry, skyMaterial );// theskybox.position.y = skyboxConst * 0.4 ;ABWorld.scene.add( theskybox );// We are inside a giant cube// can start the run loopABWorld.render();
AB.removeLoading();ABRun.runReady =true;}function resetEnemyPos(){var i,j;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;}// --- 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)}// --- take actions -----------------------------------function getEnemyAction(){// move towards agent // after n failed moves, start making random moves if( stuckfor >2)return( AB.randomIntAtoB (0,3));if( AB.randomBoolean()){if( ej < aj )return( ACTION_UP );if( ej > aj )return( ACTION_DOWN );}else{if( ei < ai )return( ACTION_RIGHT );if( ei > ai )return( ACTION_LEFT );}return( AB.randomIntAtoB (0,3));}function moveLogicalEnemy( a ){var i = ei;var j = ej;if( a == ACTION_LEFT ) i--;elseif( a == ACTION_RIGHT ) i++;elseif( a == ACTION_UP ) j++;elseif( a == ACTION_DOWN ) j--;if( occupied(i,j))
stuckfor++;else{
stuckfor =0;
ei = i;
ej = j;}}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--;elseif( a == ACTION_RIGHT ) i++;elseif( a == ACTION_UP ) j++;elseif( a == ACTION_DOWN ) j--;if(! occupied(i,j)){
ai = i;
aj = j;}}// --- score: -----------------------------------function badstep()// is the enemy within one square of the agent{if((Math.abs(ei - ai)<2)&&(Math.abs(ej - aj)<2))returntrue;elsereturnfalse;}function agentBlocked()// agent is blocked on all sides {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 status =" Step: "+ABRun.step +" x = ("+ x.toString()+") "+" a = ("+ a +") ";
$("#user_span1").html( status );}function updateStatusAfter()// agent and enemy have moved, can calculate score{// new state after both have movedvar y = self.getState();var score =( goodsteps /ABRun.step )*100;var status =" y = ("+ y.toString()+") "+" <BR> Bad steps: "+ badsteps +" Good steps: "+ goodsteps +" Score: "+ score.toFixed(2)+"% ";
$("#user_span2").html( status );}//--- public functions / interface / API ----------------------------------------------------------this.newRun =function(){
AB.loadingScreen();ABRun.runReady =false;
badsteps =0;
goodsteps =0;ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
loadResources();// aynch file loads // calls initScene() when it returns };this.getState =function(){var x =[ ai, aj, ei, ej ];return( x );};this.takeAction =function( a ){
updateStatusBefore(a);// show status line before moves
moveLogicalAgent(a);if((ABRun.step %2)==0)// slow the enemy down to every nth step{var e = getEnemyAction();
moveLogicalEnemy(e);}if( badstep()) badsteps++;else goodsteps++;
drawAgent();
drawEnemy();
updateStatusAfter();// show status line after moves if( agentBlocked())// if agent blocked in, you get BLOCKPUNISH bad steps and the problem is reset {
badsteps = badsteps + BLOCKPUNISH;ABRun.step =ABRun.step + BLOCKPUNISH;
$("#user_span3").html(" <br> <font color=red> <B> Agent blocked. "+ BLOCKPUNISH +" extra bad steps and reset enemy. </B> </font> ");
soundAlarm();// enemy is moved to a random square
resetEnemyPos();}};this.endRun =function(){
$("#user_span3").html(" <br> <font color=green> <B> Run over. </B> </font> ");};this.getScore =function(){// only called at end - do not use ABRun.step because it may have just incremented past AB.maxStepsvar s =( goodsteps / AB.maxSteps )*100;// float like 93.4372778 var x =Math.round (s *100);// 9344return( x /100);// 93.44};}//---- end of World class -------------------------------------------------------function soundAlarm(){var alarm =newAudio( SOUND_ALARM );
alarm.play();// play once, no loop }