// Cloned by Jack O'Brien on 22 Nov 2020 from World "Cat and Mouse" by Jack O'Brien // Please leave this clone trail here.// Cloned by Jack O'Brien on 7 Nov 2020 from World "Complex World" by Starter user // Please leave this clone trail here.// ==== Starter World =================================================================================================// This code is designed for use on the Ancient Brain site.// This code may be freely copied and edited by anyone on the Ancient Brain site.// To include a working run of this program on another site, see the "Embed code" links provided on Ancient Brain.// ====================================================================================================================// =============================================================================================// More complex starter World // 3d-effect Maze World (really a 2-D problem)// Movement is on a semi-visible grid of squares //// This more complex World shows:// - Skybox// - Internal maze (randomly drawn each time)// - Enemy actively chases agent// - Music/audio// - 2D world (clone this and set show3d = false)// - User keyboard control (clone this and comment out Mind actions to see)// =============================================================================================// =============================================================================================// 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 score zero.// =============================================================================================// ===================================================================================================================// === 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/jobrien14/alleyway.jpg';const TEXTURE_MAZE ='/uploads/jobrien14/trashcan2.png';const TEXTURE_AGENT ='/uploads/jobrien14/mouse.png';const TEXTURE_ENEMY ='/uploads/jobrien14/angry_cat.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 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 =2000;// 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 maxRadiusConst = MAXPOS *10;// maximum distance from camera we will render things //--- change ABWorld defaults: -------------------------------ABHandler.MAXCAMERAPOS = maxRadiusConst ;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 // https://threejs.org/docs/#api/en/loaders/CubeTextureLoader // 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"// ];// space skybox, credit:// http://en.spaceengine.org/forum/21-514-1// x,y,z labelled differentlyconst 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.// My codevar myGrid =newArray(gridsize);// Open and closed setvar openSet =[];var closedSet =[];varPath=[];varBest=[]function spot(x, y){// locationthis.i = x;this.j = y;// Wall or Mazwthis.wall =false;this.maze =false;// f, g, and h values for A*this.f =0;this.g =0;this.h =0;// Neighbors// Where did I come from?this.previous =undefined;// Figure out who my neighbors arethis.addNeighbors =function(gridsize){this.neighbors =[];var i =this.i;var j =this.j;if(i < gridsize -1)this.neighbors.push(myGrid[i +1][j]);if(i >1)this.neighbors.push(myGrid[i -1][j]);if(j < gridsize -1)this.neighbors.push(myGrid[i][j +1]);if(j >1)this.neighbors.push(myGrid[i][j -1]);if(i >1&& j >1)this.neighbors.push(myGrid[i -1][j -1]);if(i < gridsize -1&& j >1)this.neighbors.push(myGrid[i +1][j -1]);if(i >1&& j < gridsize -1)this.neighbors.push(myGrid[i -1][j +1]);if(i < gridsize -1&& j < gridsize -1)this.neighbors.push(myGrid[i +1][j +1]);}this.addBlockingNeighbors =function(gridsize){this.blockingNeighbors =[];var i =this.i;var j =this.j;if(i < gridsize -1)this.blockingNeighbors.push(myGrid[i +1][j]);if(i >1)this.blockingNeighbors.push(myGrid[i -1][j]);if(j < gridsize -1)this.blockingNeighbors.push(myGrid[i][j +1]);if(j >1)this.blockingNeighbors.push(myGrid[i][j -1]);}this.addDiagonalNeighbors =function(gridsize){this.diagonalNeighbors =[];var i =this.i;var j =this.j;if(i >1&& j >1)this.diagonalNeighbors.push(myGrid[i -1][j -1]);if(i < gridsize -1&& j >1)this.diagonalNeighbors.push(myGrid[i +1][j -1]);if(i >1&& j < gridsize -1)this.diagonalNeighbors.push(myGrid[i -1][j +1]);if(i < gridsize -1&& j < gridsize -1)this.diagonalNeighbors.push(myGrid[i +1][j +1]);}}//Drawing and Console output related functionsfunction listNeighbors(spot){
console.log(spot.neighbors.length +" Neighbors:");for(let i=0; i<= spot.neighbors.length -1; i++){
console.log("X: "+ spot.neighbors[i].i +"; Y: "+ spot.neighbors[i].j);}}function listPath(path){
console.log("Path: ");for(let i=0; i<= path.length -1; i++){
console.log("X: "+ path[i].i +"; Y: "+ path[i].j);}}function drawPath(Path, colorLine){// console.log("In drawPath");var geometry =new THREE.Geometry();var material =new THREE.LineBasicMaterial({ color: colorLine });if(Path!==null){if(Path.length >1){// console.log("Path length > 1");for(let i =0; i <Path.length-1; i++){
geometry.vertices.push(translate(Path[i].i,Path[i].j));
geometry.vertices.push(translate(Path[i+1].i,Path[i+1].j));}var line =new THREE.LineSegments( geometry, material );ABWorld.scene.add(line);
setTimeout(()=>{
geometry.dispose();
material.dispose();ABWorld.scene.remove( line );}, AB.clockTick);}}}function distance(spot1, spot2){returnMath.sqrt(Math.pow(spot2.i -spot1.i,2)+Math.pow(spot2.j -spot1.j,2));}//--- 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 squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;var BOXHEIGHT;// 3d or 2d box height 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;// enemy and agent position on squaresvar ei, ej, ai, aj;var enemyLocation;var agentLocation;var badsteps;var goodsteps;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();});}function asynchFinished()// all file loads returned {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);
myGrid[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;
myGrid[i][j]=new spot(i, j);
myGrid[i][j].wall =true;
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;
myGrid[i][j]=new spot(i, j);}// 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 ;
myGrid[i][j].maze =true;
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);}// All the neighborsfor(var i =0; i <= gridsize-2; i++)for(var j =0; j <= gridsize-2; j++){
myGrid[i][j].addNeighbors(gridsize);
myGrid[i][j].addBlockingNeighbors(gridsize);}// set up enemy // start in random locationdo{
i = AB.randomIntAtoB(1,gridsize-2);
j = AB.randomIntAtoB(1,gridsize-2);}while( occupied(i,j));// search for empty square
ei = i;
ej = j;
enemyLocation =new spot(ei, ej);
enemyLocation.addNeighbors(gridsize);
console.log('Enemy location (using spot): x = '+ enemyLocation.i +'; y = '+ enemyLocation.j);// listNeighbors(enemyLocation);// openSet starts with beginning only// openSet.push(enemyLocation);// console.log(enemyLocation.neighbors.toString());
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;
agentLocation =new spot(ai, aj);
agentLocation.addNeighbors(gridsize);
agentLocation.addBlockingNeighbors(gridsize);// agentLocation.addDiagonalNeighbors(gridsize);
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;// console.log("Test: " + myGrid[0][0].x.toString());// start the run loop});}function resetValues(Values){for(let i =0; i<Values.length; i++){Values[i].f =0;Values[i].g =0;Values[i].h =0;}}// --- 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)}//Bresenham's algorithmfunction lowLine(p1, p2){
let diffX = p2.i - p1.i;
let diffY = p2.j - p1.j;
let yi =1;if(diffY <0){
yi =-1;
diffY =-diffY;}
let D =(2* diffY)- diffX;
let y = p1.j;
let result =[]for(var x = p1.i; x<= p2.i; x++){
result.push(new spot(x, y));if(D >0){
y = y + yi;
D = D +(2*(diffY - diffX));}else{
D = D +(2*diffY);}}return result;}function highLine(p1, p2){
let diffX = p2.i - p1.i;
let diffY = p2.j - p1.j;
let xi =1;if(diffX <0){
xi =-1;
diffX =-diffX;}
let D =(2* diffX)- diffY;
let x = p1.i;
let result =[]for(var y = p1.j; y<= p2.j; y++){
result.push(new spot(x, y));if(D >0){
x = x + xi;
D = D +(2*(diffX - diffY));}else{
D = D +(2*diffX);}}return result;}function bresenhams(p1, p2){var output =[];if(Math.abs(p2.j - p1.j)<Math.abs(p2.i - p1.i)){if(p1.i > p2.i)
output = lowLine(p2, p1);else
output = lowLine(p1, p2);}else{if(p1.j > p2.j)
output = highLine(p2, p1);else
output = highLine(p1, p2);}return output;}//End Bresenham's algorithm// A* searchfunction aStar(a, b){var testPath = bresenhams(a, b);var numBlocks = countBlocks(testPath);var distance =Math.sqrt(Math.pow(a.i -b.i,2)+Math.pow(a.j -b.j,2));return distance +(numBlocks*0.75);// return distance(a, b);}function removeFromArray(arr, elt){// Could use indexOf here instead to be more efficientfor(var i = arr.length -1; i >=0; i--)if(arr[i]== elt)
arr.splice(i,1);}function path(){
openSet=[];
closedSet=[];// enemyLocation.f = aStar(enemyLocation, agentLocation);
enemyLocation =new spot(ei, ej);
enemyLocation.addNeighbors(gridsize);
openSet.push(enemyLocation);while(openSet.length >0){var winner =0;for(var i =0; i < openSet.length; i++)if(openSet[i].f < openSet[winner].f){
winner = i;// console.log("TEST");}var current = openSet[winner];// var distCToAgent = aStar(current, agentLocation);// Did I finish?if(agentLocation.blockingNeighbors.includes(current)){// noLoop();// console.log("success - found path");
resetValues(closedSet);Path=[];var temp = current;Path.push(temp);while(temp.previous){// console.log("X: " + temp.previous.i + " Y: " + temp.previous.j + " added to path");Path.push(temp.previous);
temp = temp.previous;}// console.log("Path length: " +Path.length);// listPath(Path);returnPath;}// else if (agentLocation.neighbors.includes(current)) // {// // noLoop();// // console.log("success - found path");// resetValues(closedSet);// Path = [];// var temp = current;// Path.push(temp);// while (temp.previous) // {// // console.log("X: " + temp.previous.i + " Y: " + temp.previous.j + " added to path");// Path.push(temp.previous);// temp = temp.previous;// }// // console.log("Path length: " +Path.length);// // listPath(Path);// return Path;// }// Best option moves from openSet to closedSet
removeFromArray(openSet, current);
closedSet.push(current);// Check all the neighborsvar neighbors = current.neighbors;//--- start of for loop -----------// console.log("Current neighbors: ");// listNeighbors(current);if(neighbors){for(var i =0; i < neighbors.length; i++){var neighbor = neighbors[i];var distNToAgent = aStar(neighbor, agentLocation);// Valid next spot?!closedSet.includes(neighbor) && && distNToAgent < distCToAgentif(!occupied(neighbor.i, neighbor.j)&&!closedSet.includes(neighbor)){// console.log("Spot " + i + " is not occupied");var tempG = current.g + aStar(neighbor, current);// Is this a better path than before?var newPath =false;if(openSet.includes(neighbor)){// console.log("Current node is present in openSet");if(tempG < neighbor.g){// console.log("tempG: " + tempG + " , neighbor G: " + neighbor.g);// console.log("Comparing G of current node to neighbor " + i + " , current G is less than neighbors G");
neighbor.g = tempG;
newPath =true;}// console.log("tempG: " + tempG + " , neighbor G: " + neighbor.g);// console.log("Comparing G of current node to neighbor " + i + " , current G is greater than neighbors G");// removeFromArray(openSet, neighbor);// closedSet.push(neighbor);}else//if(distCToAgent > distNToAgent){
neighbor.g = tempG;
newPath =true;
openSet.push(neighbor);// console.log("Current node is not present in openSet and is closer to agent, neighbor G is set to tempG. openSet now contains " + openSet.length + " points.");}// Yes, it's a better pathif(newPath){// console.log("New Path set to true, neighbors h and f values are set using heuristic");
neighbor.h = aStar(neighbor, agentLocation);
neighbor.f = neighbor.g + neighbor.h;
neighbor.previous = current;}}}}//--- end of for loop -----------// else // {// console.log('fail - no path exists');// // noLoop();// return null;// }Path=[];var temp = current;Path.push(temp);while(temp.previous){// console.log("X: " + temp.previous.i + " Y: " + temp.previous.j + " added to path");Path.push(temp.previous);
temp = temp.previous;}// console.log("Path length: " +Path.length);// listPath(Path);
drawPath(Path,0xff0000);}}function countBlocks(path){var count =0;// console.log("TEST: " + path.length);for(let i =0; i<path.length; i++){if(myGrid[path[i].i][path[i].j].maze ===true)
count++;}return count;}// --- take actions -----------------------------------function moveLogicalEnemy(Path){// move towards agent // put some randomness in so it won't get stuck with barriers var move = path();
drawPath(move,0x00ff00);var temp =[]
temp.push(move[0]);
temp.push(move[move.length-1]);
drawPath(temp,0x0000FF);var testPath = bresenhams(move[0], move[move.length-1]);
drawPath(testPath,0xFFD700);// listPath(testPath);// console.log(countBlocks(testPath));for(let x=0; x<move.length-1; x++){
drawPath(move,0x00ff00);
ei = move[x].i;
ej = move[x].j;}}function moveLogicalAgent( a )// this is called by the infrastructure that gets action a from the Mind {var i = ai;var j = aj;// console.log("Distance: " + a);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;
agentLocation =new spot(ai, aj);
agentLocation.addNeighbors(gridsize);
agentLocation.addBlockingNeighbors(gridsize);}}// --- 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 )returntrue;// not ready yet // if not one of our special keys, send it to default key handling:if(! ourKeys ( event ))returntrue;// 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();returnfalse;}// --- 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, run over{var score =0;if(occupied (ai-1,aj))
score ++;if(occupied (ai+1,aj))
score ++;if(occupied ( ai,aj+1))
score++;if(occupied ( ai,aj-1))
score++;// if(occupied (ai-1,aj-1))// score ++;// if(occupied ( ai+1,aj-1))// score++;// if(occupied ( ai-1,aj+1))// score++;// if(occupied (ai+1,aj+1))// score ++;return score;// 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 = AB.world.getState();
removeFromArray(x, x[x.length-1]);// removeFromArray(x, x[-1]);// removeFromArray(a, a[-1]);
AB.msg (" Step: "+ AB.step +" x = ("+ x.toString()+") a = ("+ a +") ");}function updateStatusAfter()// agent and enemy have moved, can calculate score{// new state after both have movedvar y = AB.world.getState();
removeFromArray(y, y[y.length-1]);var score =( goodsteps / AB.step )*100;
AB.msg (" y = ("+ y.toString()+") <br>"+" Bad steps: "+ badsteps +" Good steps: "+ goodsteps +" Score: "+ score.toFixed(2)+"% ",2);}
AB.world.newRun =function(){
AB.loadingScreen();
AB.runReady =false;
badsteps =0;
goodsteps =0;if( show3d ){
BOXHEIGHT = squaresize;ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}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, myGrid];return( x );};
AB.world.takeAction =function( a ){
updateStatusBefore(a);// show status line before moves
moveLogicalAgent(a);// console.log('Agent location: x = ' + ai + '; y = ' + aj);if(( AB.step %2)==0){// slow the enemy down to every nth step
moveLogicalEnemy();}if( badstep()) badsteps++;else goodsteps++;
drawAgent();
drawEnemy();
updateStatusAfter();// show status line after moves if( agentBlocked()>=4)// if agent blocked in, run over {
AB.abortRun =true;
goodsteps =0;// you score zero as far as database is concerned // musicPause();// soundAlarm();}};
AB.world.endRun =function(){// musicPause(); if( AB.abortRun ) AB.msg (" <br> <font color=red> <B> Agent 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.maxStepsvar s =( goodsteps / AB.maxSteps )*100;// float like 93.4372778 var x =Math.round (s *100);// 9344return( x /100);// 93.44};// --- music and sound effects ----------------------------------------// var backmusic = AB.backgroundMusic ( MUSIC_BACK );// function musicPlay() { backmusic.play(); }// function musicPause() { backmusic.pause(); }function soundAlarm(){var alarm =newAudio( SOUND_ALARM );
alarm.play();// play once, no loop }