const WALLMOVENO = 10 ; // 0; //CMC - number of wall blocks that will move, from 0 upwards.
const WALLMOVETICK = 10 ; // 0; //CMC - wall blocks move every WALLMOVETICK timesteps. Zero for never
// Colin McCabe (CMC) Note: as I have put no bounds on the agent this means Picard can escape and crash the program... I was not sure if I was supposed to also change the agent code...
// Cloned by Colin McCabe on 10 Nov 2022 from World "NG: Locutus versus The Borg (clone by Colin McCabe)" by Colin McCabe
// Please leave this clone trail here.
// Cloned by Colin McCabe on 22 Oct 2022 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; // 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/tesla/stars02.png' ; // CMC just part of the picture
const TEXTURE_MAZE = '/uploads/tesla/moon.jpg' ; // CMC seemed like a good idea at the time... but not as a cube
const TEXTURE_AGENT = '/uploads/tesla/picard04.png' ; // CMC seemed like teh best good guy for teh BORG
const TEXTURE_ENEMY = '/uploads/tesla/borg01.jpg' ; // CMC with so many cubes how could I not go with one of teh most famous cubes.. cubes in sapce!
const TEXTURE_MWALL = '/uploads/tesla/spacerock.jpg'; // CMC And something for the 5th loader I created, but not needed ....
// 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/tesla/Star-Trek_Next_Generation.mp3' ; // CMC I couldn't help myself.. as the borg first appeared in star trek next gen it seemed appropriate
const SOUND_ALARM = '/uploads/tesla/tng_weapons_clean.mp3' ; // CMC If you hear this Picard is dead :-(
// credits:
// http://www.dl-sounds.com/royalty-free/defense-line/
// http://soundbible.com/1542-Air-Horn.html
const gridsize = 20; // number of squares along side of world CMC it was originally 20
const NOBOXES = Math.trunc ( (gridsize * gridsize) / 10 ); // CMC it was originally 10
// 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 = 0xFFFFFF; // a number, not a string CMC this was originally 0xddffdd. I didn't notice any difference however....
const startRadiusConst = MAXPOS * 0.95 ; // distance from centre to start the camera at CMC this was 0.8
const maxRadiusConst = MAXPOS * 10 ; // maximum distance from camera we will render things
// CMC The next section lists the global variables added by CMC
// CMC Some of these variables are courtesy of Daniel Shiffman's from "A star" by "Coding Train" project... okay so Im lazy...
var openSet = []; // CMC Just like Daniel, keep a list of all A* openset values i.e. what spots to check next in the list
var closedSet = []; // CMC Just like Daniel, keep a list of all A* closedset values i.e. the lsit of spots we already checked and found no agent
const PATHON=true; // CMC this flag either activates or deactivates the enemy (aka Borg) path
var pathArray = []; // CMC create a place to store all my enemy path spots based off the current spot previous values and their previous values and so on
var vPathArray = []; // CMC store the path a set of translated point vectors in 3D space
var vMaterial; // CMC a place to store my line details, wanted it to be different from the mesh material variables (same for geometry, using v made it different ...)
var vGeometry; // CMC a place to store the memory where my path line will be stored
var pathLine; // CMC used to instantiate my path line to AB
var wallPointArray = []; // CMC a temporary place to store all wall spots on the grid
var wallMovers = []; // CMC a place to store all wall spots that will be going walkies around the grid
var start; // CMC the spot where the Borg start from
var agentLoc; // CMC the spot where Picard is now
var current; // CMC the spot where the Borg are now
var winner; // CMC sets the best next spot from the openset list
var beginit = true; // CMC used for any first loop initialization that I needed through the various testing cycles
var walli , wallj; // CMC lets find wally! only joking, these store the point values for each random wall in my move wall funciton
var validSpots = []; // CMC stores valid and legal next wall spot locaiton for the random moving walls
// End of global variables added by CMC
//--- 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
// CMC I needed a space theme...so disabling these lovely pics..
/*
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 differently
// CMC But enabling this beautiful stary sky..
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;
var wall_texture, agent_texture, enemy_texture, maze_texture;
// enemy and agent position on squares
var ei, ej, ai, aj;
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 ) return true;
else return false;
}
//--- grid system -------------------------------------------------------------------------------
// my numbering is 0 to gridsize-1
function occupied ( i, j ) // is this square occupied
{
if (i >= 0 && i < gridsize && j >= 0 && j < gridsize) // CMC added this to make more generalized for wall grid out-of-bounds checks
{
if ( ( ei == i ) && ( ej == j ) ) return true; // variable objects
if ( ( ai == i ) && ( aj == j ) ) return true;
if ( GRID[i][j].gridrole == GRID_WALL ) return true; // fixed objects
if ( GRID[i][j].gridrole == GRID_MAZE ) return true;
return false;
}
else return false;
}
// 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;
}
// CMC New funciton added by CMC
// CMC Need to do some added prepearation work to build the grid of neighbours
// CMC a lot of this code is courtesy of Daniel Shiffman's from "A star" by "Coding Train" project
function Spot(i, j)
{
this.i = i; // Location
this.j = j; // Location
this.f = 0; // f, g, and h values for A*
this.g = 0; // f, g, and h values for A*
this.h = 0; // f, g, and h values for A*
this.neighbors = []; // Neighbors
this.gridrole = GRID_BLANK;
this.previous = undefined; // Where did I come from?
this.addNeighbors = function(GRID)
{
var i = this.i;
var j = this.j;
if (i < gridsize - 1) GRID[i][j].neighbors.push(GRID[i + 1][j]);
if (i > 0) GRID[i][j].neighbors.push(GRID[i - 1][j]);
if (j < gridsize - 1) GRID[i][j].neighbors.push(GRID[i][j + 1]);
if (j > 0) GRID[i][j].neighbors.push(GRID[i][j - 1]);
}
}
//=== heuristic ===========================
// this must be always optimistic - real time will be this or longer
// CMC Some of these variables and code are courtesy of Daniel Shiffman's from "A star" by "Coding Train" project...
function heuristic ( aa, bb) // CMC Normally I would have removed this as its a duplicate of gfn and howFar functions
{
return ( Math.abs(aa.i - bb.i) + Math.abs(aa.j - bb.j) );
// can only go across and down
// so this is optimistic
}
function howFar( here, tothere) // CMC its the exact same as the original gfn & heuristic functions but kept the oldies to keep as much of original logic as possible
{ // CMC DEBUG console.log('howFar function: neighbour= '+a.i+','+a.j+' agentLoc= '+b.i+','+b.j+' The result= '+(Math.abs(a.i - b.i) + Math.abs(a.j - b.j)));
return ( Math.abs(here.i - tothere.i) + Math.abs(here.j - tothere.j) );
}
// Function to delete element from the array
// CMC Some of these variables and code are courtesy of Daniel Shiffman's from "A star" by "Coding Train" project... okay so Im lazy...
function removeFromArray(arr, elt)
{
// Could use indexOf here instead to be more efficient
for (var i = arr.length - 1; i >= 0; i--)
if (arr[i] == elt)
arr.splice(i, 1);
}
function clearGrid() // CMC This is a hard reset of everything. Usually means there is no access to the Agent i.e. explored all options
{
for ( i = 0; i < gridsize ; i++ ) // CMC clean down all h's and f's and g's
for ( j = 0; j < gridsize ; j++ )
{
GRID[i][j].h=0;
GRID[i][j].f=0;
GRID[i][j].g=0;
}
}
//=== g function =======================================
// g function is known distance from start along a known path
// we work this out by adding up adjacent squares in a cumulative fashion
// note g definition should be separate from h definition (in earlier version they were confused)
// CMC Some of these variables and code are courtesy of Daniel Shiffman's from "A star" by "Coding Train" project... okay so Im lazy...
function gfn ( aa, bb ) // CMC Normally I would have removed this as its a duplicate of heuristic and howFar functions
{
return ( Math.abs(aa.i - bb.i) + Math.abs(aa.j - bb.j) ); // else go across and down
}
function available ( aSpot ) // CMC is this square occupied, not checking for the Agent
{
if ( aSpot.gridrole == GRID_WALL ) return false; // fixed objects WALL
if ( aSpot.gridrole == GRID_MAZE ) return false; // fixed objects MAZE
return true;
}
// -----------
function initScene() // all file loads have returned
{
var i,j, shape, thecube;
// set up GRID as 2D array
for ( i = 0; i < gridsize ; i++ )
GRID[i] = new Array(gridsize);
// CMC instantiate the grid map
for ( i = 0; i < gridsize ; i++ )
for ( j = 0; j < gridsize ; j++ )
GRID[i][j] = new Spot(i, j);
// CMC figure out all the neighbors
for ( i = 0; i < gridsize ; i++ )
for ( j = 0; j < gridsize ; j++ )
GRID[i][j].addNeighbors(GRID);
// 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].gridrole = GRID_WALL; // CMC make sure there is a property on every spot that says whether its a wall or maze or blank
wallPointArray.push(new gridPoint(i,j)); // CMC lets capture all wall locations in an array to use later to setup the moving walls part
}
else
GRID[i][j].gridrole = GRID_BLANK; // CMC make sure there is a property that says whehter its a wall or maze or blank (i.e. available to move to)
if(WALLMOVENO !== 0)
{
randomWallInitializer(); // CMC this function separates out the moving walls that we selected randomly into a new list of wallMovers, from the stationary walls
for ( i = 0; i < wallMovers.length ; i++ ) // CMC this function initializes the moving walls now captured in the new list of wallMovers
{
wallMovers[i].shape = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );
wallMovers[i].thecube = new THREE.Mesh( wallMovers[i].shape );
wallMovers[i].thecube.material = new THREE.MeshBasicMaterial( { color : 0x00FF00 } ); // CMC Lets highlight the walls in bright green, only initally, that will move: The old value { map: wall_texture }
wallMovers[i].thecube.position.copy ( translate(wallMovers[i].i, wallMovers[i].j) ); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(wallMovers[i].thecube);
}
}
for ( i = 0; i < wallPointArray.length ; i++ ) // CMC this is the list of walls that are staying put but still need setting up as I just moved them out of teh previous setup loop
{
shape = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );
thecube = new THREE.Mesh( shape );
thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } );
thecube.position.copy ( translate(wallPointArray[i].i, wallPointArray[i].j) ); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(thecube);
}
// 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].gridrole = GRID_MAZE ;
shape = new THREE.SphereGeometry ( squaresize/2,50,50); // original 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;
start = GRID[ei][ej]; // Added by CMC to capture the BORG's starting point
current = GRID[ei][ej]; // Added by CMC to capture the BORG's first current point
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.CylinderGeometry ( squaresize/2,squaresize/2,squaresize,100 ); //original ... 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
});
}
// CMC new random wall initializer
// CMC this creates a new list of moving walls using AB.randomIntAtoB and puts these wall movers into wallMovers
function randomWallInitializer()
{
for(var i = 0; i < WALLMOVENO ; i++)
{
var chosenOne=AB.randomIntAtoB(0,wallPointArray.length-1); // CMC randomly pick the walls to be moved, the chosen ones....
wallMovers.push(new wallSpot(wallPointArray[chosenOne].i, wallPointArray[chosenOne].j)); // CMC store them in my wallmovers list
wallPointArray.splice(chosenOne,1); // CMC need to ensure we have no duplicates, so this part removes the chosen walls after they have been randomly chosen
}
// console.log('wallPointArray len = '+wallPointArray.length+' The wallMovers are: '+showallpointsinSet(wallMovers)+' Length= '+wallMovers.length);
}
// CMC I used this for some debug to check the point values in the spot objects, its a bit of a rush job...
function showallpointsinSet(thelist)
{
var arrtest = [];
var count = 0;
for( i = 0; i < thelist.length ; i++ )
{
arrtest[count] = thelist[i].i;
arrtest[count+1] = ',';
arrtest[count+2] = thelist[i].j;
if (i<thelist.length-1) arrtest[count+3] = ' : ';
count+=4;
}
var strtest = arrtest.join("");
return strtest;
}
// CMC I set this up for my path line. Of all the facny options I tried to get the line working this was the easiest.
function gridPoint(i, j) {
this.i = i;
this.j = j;
}
// CMC this is a quirky function and a bit of a wierd way of doing this but I was stuck for time and left it...
// CMC I had some great ideas on how to create moving walls but ended up rushing this in the end... it mostly works..
function wallSpot(i, j) {
this.i = i;
this.j = j;
this.shape = undefined;
this.thecube = undefined;
}
// --- 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)
}
// function drawPath( pi , pj ) // CMC given pi, pj list of previous values, draw it
// CMC lets finally draw the new walls thats what this function does
// CMC I could have combined this into a single fuction for moving walls but seemed like the original program preferred to separate out moving and drawing, so I did the same..
function drawNewWall() // CMC given teh list of wallMovers
{
for ( var i = 0; i < wallMovers.length ; i++ )
{
wallMovers[i].shape = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );
wallMovers[i].thecube = new THREE.Mesh( wallMovers[i].shape );
wallMovers[i].thecube.material = new THREE.MeshBasicMaterial( { map: wall_texture } ); // CMC old value { color : 0x00FF00 }
wallMovers[i].thecube.position.copy ( translate(wallMovers[i].i, wallMovers[i].j) ); // translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
ABWorld.scene.add(wallMovers[i].thecube);
}
}
// CMC this is the new function to draw the enemy (aka Borg) path similar to drawNewWall I separated this out from the function that identifies the path previous values which I left in the enemy moving function
function drawPath() // CMC given pi, pj list of previous values, draw it
{
ABWorld.scene.remove(pathLine); // CMC Clear old path away otherwise we end up with Spaghetti on the grid. I preferred a red line hence 0xFF0000 below (I could of soft coded this value but....it works)
vMaterial = new THREE.LineBasicMaterial( { color: 0xFF0000, linewidth:10 } ); // CMC Bug... Due to limitations of the OpenGL Core Profile with the WebGL renderer on most platforms linewidth will always be 1 regardless of the set value.
vGeometry = new THREE.BufferGeometry().setFromPoints( vPathArray );
pathLine = new THREE.Line( vGeometry, vMaterial );
ABWorld.scene.add(pathLine);
}
// --- take actions -----------------------------------
// CMC Some of these variables and lots of the code are courtesy of Daniel Shiffman's from "A star" by "Coding Train" project...
function moveLogicalEnemy()
{
var i, j;
var keepGoing = 0; // CMC Tells the borg how far away they are from Picard. a value of 1 means the borg are right on top of Picard.. the problem is if Picard moved into a spot in the closed list
agentLoc = GRID[ai][aj]; // CMC agentLoc I need to know where the ever-changing end goal is (This was previously the end in Daniel Shiffman's code )
winner = 0; // CMC lets get things started, still used teh same way as before
console.log('Start BORG move loop!'); // CMC I left this debug as its no harm and still useful if something goes wrong
if(beginit===true) // CMC set up the current value if this is our first ever run
{
openSet.push(start);
beginit = false;
}
if (closedSet.includes(agentLoc)) // CMC I did't want Picard getting passed the Borg and escaping, may as well start over again..its debatable whether this is really needed...
{ // CMC This snippet wipes most values and start from scratch...
console.log('Starting all over again...The agent just passed me by and I missed him i.e. Picard dodged the borg!!'); // CMC I left this debug as its no harm and still useful if something goes wrong
clearGrid(); // CMC Start again as the openSet has been run down and the agent has moved, got to remove the g values at least otherwise this screws up the paths previous map-back part below
current = start; // CMC put the Borg back where they started..
openSet = []; // CMC cleardown any residual openset spots there's no point keeping them in a clear down
openSet.push(current); // CMC got to start somewhere....and need at least one value to start with
closedSet = []; // CMC got to clear all the closedSet values as the agent can hide there
}
// console.log('1st start = '+start.i+','+start.j+' agentLoc = '+agentLoc.i+','+agentLoc.j+' Distance='+howFar(current, agentLoc));
keepGoing = howFar(current, agentLoc); // CMC Figures out how far away is Picard from the borg
if(keepGoing > 1 ) // CMC If Picard is still far away keep going...if I'm not on Picard's ass we have work to do, got to get that score lower as quick as possible
{
// Best next option
for (var i = 0; i < openSet.length; i++)
if (openSet[i].f < openSet[winner].f)
winner = i;
current = openSet[winner];
removeFromArray(openSet, current); // Best option moves from openSet to closedSet
closedSet.push(current);
var neighbors = current.neighbors; // Check all the neighbors
//--- start of for loop -----------
for (var i = 0; i < neighbors.length; i++)
{
var neighbor = neighbors[i];
// Valid next spot?
if (!closedSet.includes(neighbor) && available(neighbor))
{
var g = current.g + gfn ( neighbor, current ); // add up g values in cumulative way
// Is this a better path than before?
var newPath = false;
if (openSet.includes(neighbor))
{
if (g < neighbor.g)
{
neighbor.g = g;
newPath = true;
}
}
else
{
neighbor.g = g;
newPath = true;
openSet.push(neighbor);
}
// Yes, it's a better path
if (newPath)
{
neighbor.h = heuristic ( neighbor, agentLoc );
neighbor.f = neighbor.g + neighbor.h;
neighbor.previous = current;
}
}
}
//--- end of for loop -----------
if (openSet.length === 0) // CMC we've run out of options but we're still going to keep going till the clock runs down. I didn't like to have teh borg jsut stitting there
{
console.log('No path to the Agent! Weve run out of Openset values.... so lets start all over again :-( !!');
AB.msg ( " <br> <font color=red> <B> Alert the Borg are trapped. They appear to be boxed in! But... they'll try again... </B> </font> ", 3 ); // CMC I couldnt resist putting this in..
clearGrid(); // CMC Start again as the openSet has been run down and the agent has moved
current = start;
openSet = [];
openSet.push(current);
closedSet = [];
beginit = true;
}
// console.log('current = '+current.i+','+current.j+' agentLoc = '+agentLoc.i+','+agentLoc.j+' Distance='+howFar(current, agentLoc)+' Openset len ='+openSet.length+' closedset len= '+closedSet.length);
if ( ! occupied(current.i,current.j) ) // if no obstacle then move, else just miss a turn
{
ei = current.i;
ej = current.j;
}
} // CMC end of k loop where the borg is moving toward Picard, hopefully, the else means the borg are right on top of Picard and hopefully causing the score to drop
else // CMC Other side of the distance check loop. If the Borg get to this part I want them to do noting and stay close to Picard to get that score as low as possible...
{ // CMC The borg are one spot away from Picard!
console.log('The BORG are right on top of Picard!!');
}
if(PATHON) // CMC I thought it would be useful to be able to switch the path on or off, see PATHON at the top of the code
{ // Find the path by working backwards
pathArray=[]; // CMC a new array to store all previous best path spots
vPathArray = []; // CMC I needed to store the vector version of the list of path points as I want to put this straight into the path line drawing fuction
var temp = current;
pathArray.push(new gridPoint(temp.i,temp.j)); // CMC I went with an array of points to all previous spot values starting with current to build up the path
vPathArray.push(new translate ( temp.i,temp.j)); // CMC I wanted to be sure this line would work as a host of other attempts failed to varying degrees...
while (temp.previous)
{
temp = temp.previous;
pathArray.push(new gridPoint(temp.i,temp.j)); // CMC load the previous spots into the path array
vPathArray.push(new translate ( temp.i,temp.j)); // CMC load the previous vector version of the spots into the path array
}
}
// console.log('Draw pathArray, with length= '+pathArray.length+' the pathArray is: '+showallpointsinSet(pathArray));
}
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;
}
}
// CMC a new function to move some random wall spots
function moveLogicalWall() // CMC new wall move function
{
// console.log('moveLogicalWall start, with wallMovers length = '+wallMovers.length+' The wall movers are '+showallpointsinSet(wallMovers));
for ( var i = 0; i < wallMovers.length ; i++ )
{
validSpots = [];
walli = 0; // CMC I wanted to be really sure there was no residual values and same for j
wallj = 0;
walli = wallMovers[i].i; // CMC grab a wall spot off our lsit of random wall movers in this case the i
wallj = wallMovers[i].j; // CMC grab a wall spot off our lsit of random wall movers in this case the j
// console.log('i= '+i+' For wall = '+walli+','+wallj);
var wallNeighbors; // CMC I though it would be easer to use the same neighbor functionality from Daniel's code
wallNeighbors = GRID[walli][wallj].neighbors; // CMC Using the GRID global array to pull the neghbours for the moving wall spot
// console.log('wallNeighbors length = '+wallNeighbors.length+' All neighbours are: '+showallpointsinSet(wallNeighbors));
//--- start of 2nd for loop -----------
for (var inc = 0; inc < wallNeighbors.length; inc++) // CMC pulling all available neighbors
{
var neighbor = wallNeighbors[inc];
// Valid next spot?
if (!occupied(neighbor.i , neighbor.j)) // CMC pulling all legal neighbors
{
validSpots.push(neighbor); // CMC storing alllegal neighbors in the validspots list
}
}
// console.log('wallMovers.length = '+wallMovers.length+' For wall = '+walli+','+wallj+' validSpots len = '+validSpots.length+' All validSpots are: '+showallpointsinSet(validSpots));
if (validSpots.length !== 0) // CMC There are no valid options avaialble if validspots is zero, so jump over this wall spot for now
{ // CMC Jump over this selection as there are no valid next walls
if(validSpots.length > 1)
{
var nextSpot = AB.randomIntAtoB(0,validSpots.length-1); // CMC this is the part that makes the movement of the walls random
}
else
{
var nextSpot = 0; // CMC okay, so there is only one valid spot for the wall to move to, so lets move there anyway...
}
GRID[walli][wallj].gridrole = GRID_BLANK; // CMC the old wall spt needs to be made avaialble for Picard to escape to... the Borg are more sensible...
// console.log('Random# = '+nextSpot+' Gridrole= '+GRID[walli][wallj].gridrole);
ABWorld.scene.remove(wallMovers[i].thecube); // CMC got to make the old walls disappear
wallMovers[i].i=validSpots[nextSpot].i; // CMC move wall to its next available adn legal random (or not) spot (i value)
wallMovers[i].j=validSpots[nextSpot].j; // CMC move wall to its next available adn legal random (or not) spot (j value)
GRID[wallMovers[i].i][wallMovers[i].j].gridrole = GRID_WALL; // CMC we need to flag the spot the wall ahs moved to as occupied
// console.log('Random# = '+nextSpot+' Gridrole= '+GRID[wallMovers[i].i][wallMovers[i].j].gridrole);
} // CMC end of the loop that moves wall to its next spot
// console.log('moveLogicalWall start, with wallMovers length = '+wallMovers.length+' The wall movers are '+showallpointsinSet(wallMovers));
} // CMC end of the loop that pulls our moving wall off the list and moves it to a new spot
}
// --- 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(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();
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 moved
var y = AB.world.getState();
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 ];
return ( x );
};
AB.world.takeAction = function ( a )
{
updateStatusBefore(a); // show status line before moves
moveLogicalAgent(a);
if(WALLMOVETICK !== 0 ) // CMC if WALLMOVETICK is set to zero, do nothing... i.e. don't move any random walls
{
if ( ( AB.step % WALLMOVETICK ) == 0 ) // CMC I'm letting Picard move first...
{
moveLogicalWall();
drawNewWall(); // CMC hoping this works now
}
}
if ( ( AB.step % 2 ) == 0 ) // CMC Originally 2
moveLogicalEnemy();
if ( badstep() ) badsteps++;
else goodsteps++;
drawAgent();
drawEnemy();
if(PATHON) drawPath(); // CMC lets finally draw the path...
updateStatusAfter(); // show status line after moves
if ( agentBlocked() ) // 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.maxSteps
var s = ( goodsteps / AB.maxSteps ) * 100; // float like 93.4372778
var x = Math.round (s * 100); // 9344
return ( 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 = new Audio ( SOUND_ALARM );
alarm.play(); // play once, no loop
}