// Cloned by Vineet Sajwan on 10 Nov 2022 from World "Scary A* with path" by Vineet Sajwan // Please leave this clone trail here.// Cloned by Vineet Sajwan on 9 Nov 2022 from World "Scary A*" by Vineet Sajwan // Please leave this clone trail here.// Cloned by Vineet Sajwan on 6 Nov 2022 from World "Scary A*" by Vineet Sajwan // Please leave this clone trail here.// Cloned by Vineet Sajwan on 27 Oct 2022 from World "Complex World (clone by Vineet Sajwan)" by Vineet Sajwan // Please leave this clone trail here.// Cloned by Vineet Sajwan on 27 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.// =============================================================================================//console.log("part_1 invoked")// ===================================================================================================================// === 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/xlr8pro/stone_wall.jpg';const TEXTURE_MAZE ='/uploads/xlr8pro/asteroid.PNG';const TEXTURE_AGENT ='/uploads/xlr8pro/ufo_2.jpg';const TEXTURE_ENEMY ='/uploads/xlr8pro/chip.jpg';// credits:// http://commons.wikimedia.org/wiki/File:stone_wall.jpg// https://commons.wikimedia.org/wiki/Category:asteroid_icons// https://commons.wikimedia.org/wiki/Category:ufo_icons// http://en.wikipedia.org/wiki/chip.jpgconst MUSIC_BACK ='/uploads/xlr8pro/no_surprises.mp3';const SOUND_ALARM ='/uploads/xlr8pro/buzzer_1.mp3';// credits://http://youtube.com/no_surprises// http://soundbible.com/1542-Air-Horn.html const gridsize =20;// 20; // number of squares along side of world // const NOBOXES = Math.trunc ( (gridsize * gridsize) / 10 ); // 10 const NOBOXES =55;// 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 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.htmlconst SKYBOX_ARRAY =["/uploads/xlr8pro/pz_5.png","/uploads/xlr8pro/nz_5.png","/uploads/xlr8pro/py_5.png","/uploads/xlr8pro/ny_5.png","/uploads/xlr8pro/px_5.png","/uploads/xlr8pro/nx_5.png"];// space skybox, credit://https://jaxry.github.io/panorama-to-cubemap/ -> for converting image into skybx shape// ===================================================================================================================// === 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 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 badsteps;var goodsteps;/*
Author : Vineet Sajwan
Declared Global Variable as per requirement
*/
diagonal=!1Draw=!0
shownPath=[]const WALLMOVENO=5;const WALLMOVETICK=2;const pathcolor ='darkred';function loadResources()// asynchronous file loads - call initScene() when all finished {//console.log("part_2 invoked")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();});}/*
Author : Vineet Sajwan
Calculates the heuristic value
*/function heuristic(e,t){returnMath.abs(e.i-t.i)+Math.abs(e.j-t.j)}function asynchFinished()// all file loads returned {//console.log("part_3 invoked")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{//console.log("part_4 invoked")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 ){//console.log("part_5 invoked")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 {//console.log("part_6 invoked")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 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;
shape =new THREE.SphereGeometry( squaresize-50, BOXHEIGHT-50, squaresize-50);
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.ConeGeometry( squaresize-30,squaresize-30, BOXHEIGHT-20)
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});}// --- draw moving objects -----------------------------------function drawEnemy()// given ei, ej, draw it {//console.log("part_7 invoked")
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 {//console.log("part_8 invoked")
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)}/**
* @param {?} col
* @param {?} row
* @return {?}
*/function isWall(col, row){return GRID[col][row]== GRID_WALL || GRID[col][row]== GRID_MAZE;}/**
* Created by Vineet Sajwan
* Provides the coordinates for the neighboring cell
*/functionSpot(i, j){this.i = i;this.j = j;this.f =0;this.g =0;this.h =0;this.neighbors =[];this.wall = isWall(i, j);this.previous =void0;this.addNeighbors =function(arr){var i =this.i;var j =this.j;if(i < gridsize -1){this.neighbors.push(arr[i +1][j]);}if(i >0){this.neighbors.push(arr[i -1][j]);}if(j < gridsize -1){this.neighbors.push(arr[i][j +1]);}if(j >0){this.neighbors.push(arr[i][j -1]);}if(diagonal){if(i >0&& j >0){this.neighbors.push(arr[i -1][j -1]);}if(i < gridsize -1&& j >0){this.neighbors.push(arr[i +1][j -1]);}if(i >0&& j < gridsize -1){this.neighbors.push(arr[i -1][j +1]);}if(i < gridsize -1&& j < gridsize -1){this.neighbors.push(arr[i +1][j +1]);}}};}/**
* Author: Vineet Sajwan
* responsible for highlighting the path
*/function highlightPath(res, from){/** @type {number} */var k =0;//Created a path using ShownPath listfor(; k < res.length -1; k++){
shape =new THREE.BoxGeometry(squaresize-50,.1, squaresize-50);ShownPath=new THREE.Mesh(shape);ShownPath.material =new THREE.MeshBasicMaterial({
color :16711680});ShownPath.position.copy(translate(res[k].i, res[k].j));ABWorld.scene.add(ShownPath);
shownPath.push(ShownPath);}}// --- take actions -----------------------------------/**
* Author : Vineet Sajwan
* Implemented A*
*/function moveLogicalEnemy(){var result =newArray(gridsize);var nodes =[];for(var element =0; element < shownPath.length; element++){ABWorld.scene.remove(shownPath[element]);}// Created 2 D Array
result =newArray(gridsize);
nodes =[];
o =[];for(var j =0; j < gridsize; j++){
result[j]=newArray(gridsize);}// Intialize default value for each cellfor(var column =0; column < gridsize; column++){for(var row =0; row < gridsize; row++){
result[column][row]=newSpot(column, row);}}// Fetches neighboring cell from spot functionfor(var column1=0; column1 < gridsize; column1++){for(var row1 =0; row1 < gridsize; row1++){
result[column1][row1].addNeighbors(result);}}// set the position for enemy and mindvar start = result[ei][ej];var end = result[ai][aj];
nodes.push(start);for(var i =0; nodes.length >0;){
path =[];// if the f value of the enemy is lowerfor(j =0; j < nodes.length; j++){if(nodes[j].f < nodes[i].f){
i = j;}}var current = nodes[i];// if the current point is the end point assifned next point as a current pointif(current === end){var res =[];var next = current;
res.push(next);for(; next.previous;){
res.push(next.previous);
next = next.previous;}//inititalize the highlightPath
highlightPath(res,190);var off = res.length -1;if(!occupied(res[off -1].i, res[off -1].j)){
ei = res[off -1].i;
ej = res[off -1].j;}break;}//Delete the current node from the nodes list after selecting and rejecting as the current path
nodes.splice(i,1);
o.push(current);var neighbors = current.neighbors;
j =0;for(; j < neighbors.length; j++){var neighbor = neighbors[j];if(neighbor[j]=== end){
neighbor.previous = current;break;}if(!o.includes(neighbor)&&!neighbor.wall){//Calcultaing hte heuristicsvar ng = current.g + heuristic(neighbor, current);var h =false;if(nodes.includes(neighbor)){if(ng < neighbor.g){
neighbor.g = ng;
h =true;}}else{
neighbor.g = ng;
h =true;
nodes.push(neighbor);}if(h){//Calcultaing hte heuristics
neighbor.h = heuristic(neighbor, end);
neighbor.f = neighbor.g + neighbor.h;//Assigned the current value as the previous value
neighbor.previous = current;}}}}}function moveLogicalAgent( a )// this is called by the infrastructure that gets action a from the Mind {//console.log("part_10 invoked")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;}}// --- 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 ){//console.log("part_11 invoked")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{//console.log("part_12 invoked")if((Math.abs(ei - ai)<2)&&(Math.abs(ej - aj)<2))returntrue;elsereturnfalse;}function agentBlocked()// agent is blocked on all sides, run over{//console.log("part_13 invoked")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 {//console.log("part_14 invoked")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//console.log("part_15 invoked")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(){//console.log("part_16 invoked")
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(){//console.log("part_17 invoked")var x =[ ai, aj, ei, ej ];//console.log(x)return( x );};
AB.world.takeAction =function( a ){//console.log("part_18 invoked")//console.log(a)
updateStatusBefore(a);// show status line before moves
moveLogicalAgent(a);//console.log(AB.step)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())// 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(){//console.log("part_19 invoked")
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//console.log("part_20 invoked")var 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 }