// ==== 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.
var start,end,diagonal = true;
// Open and closed set
openSet =[];
closedSet =[];
neighbors =[];
path =[];
//---- global constants: -------------------------------------------------------
const show3d = true; // Switch between 3d and 2d view (both using Three.js)
const TEXTURE_WALL = '/uploads/shafi200/wall.png' ;
const TEXTURE_MAZE = '/uploads/starter/latin.jpg' ;
const TEXTURE_AGENT = '/uploads/starter/pacman.jpg' ;
const TEXTURE_ENEMY = '/uploads/starter/ghost.3.png' ;
// 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/sinfulsalad/Forest.mp3';
const SOUND_ALARM = '/uploads/inapo/bgmusic.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 = 100; // size of square in pixels
const 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 at
const 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/shafi200/rainbow_bk.png",
"/uploads/shafi200/rainbow_bk.png",
"/uploads/shafi200/rainbow_bk.png",
"/uploads/shafi200/rainbow_bk.png",
"/uploads/shafi200/rainbow_bk.png",
"/uploads/shafi200/rainbow_bk.png",
];
// ===================================================================================================================
// === 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;
// asynchronous file loads - call initScene() when all finished
function loadResources()
{
var e = new THREE.TextureLoader();
var t = new THREE.TextureLoader();
var o = new THREE.TextureLoader();
var a = new THREE.TextureLoader();
e.load(TEXTURE_WALL,function(e)
{
e.minFilter = THREE.LinearFilter,
wall_texture = e,asynchFinished()&&initScene();// if all file loads have returned
});
t.load(TEXTURE_AGENT,function(e)
{
e.minFilter = THREE.LinearFilter,
agent_texture = e,asynchFinished()&&initScene();
});
o.load(TEXTURE_ENEMY,function(e)
{
e.minFilter = THREE.LinearFilter,enemy_texture = e,asynchFinished()&&initScene();
});
a.load(TEXTURE_MAZE,function(e){e.minFilter = THREE.LinearFilter,maze_texture = e,asynchFinished()&&initScene();
});
}
// all file loads returned
function asynchFinished()
{
return!!(wall_texture&&agent_texture&&enemy_texture&&maze_texture)}
function occupied(e,t)
{
return ei==e&&ej==t||(ai==e&&aj==t||(e>=19||t>=19||(1==GRID[e][t].wall||1==GRID[e][t].wall)));
}
function translate(e,t)
{
var o = new THREE.Vector3();
return o.y = 0,
o.x = e*squaresize - MAXPOS/2,
o.z=t*squaresize-MAXPOS/2,
o}
// all file loads have returned
function initScene()
{
var e,t,o,a;
// set up GRID array
for (e = 0; e < gridsize ; e++)
GRID[e] = new Array(gridsize);
for(e = 0; e < gridsize;e++)
// set up walls
for (t = 0; t < gridsize ; t++)
0 == e || e == gridsize-1 || 0 == t || t == gridsize-1?(GRID[e][t] = GRID_WALL,
o = new THREE.BoxGeometry (squaresize, BOXHEIGHT, squaresize),
(
a = new THREE.Mesh(o)).material = new THREE.MeshBasicMaterial({map:wall_texture}),
a.position.copy(translate(e,t)),
ABWorld.scene.add(a),GRID[e][t] = new Spot(e,t,!0)):
(GRID[e][t] = GRID_BLANK,GRID[e][t] = new Spot(e,t,!1));
// set up maze
for ( var i = 1;
i <= NOBOXES;i++)e=AB.randomIntAtoB(1,gridsize-2),
t = AB.randomIntAtoB(1,gridsize-2),
GRID[e][t] = GRID_MAZE,
o = new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize),
(
a = new THREE.Mesh(o)).material = new THREE.MeshBasicMaterial({map:maze_texture}),
a.position.copy(translate(e,t)),
ABWorld.scene.add(a),GRID[e][t] =new Spot(e,t,!0);
// set up enemy
// start in random location
do
{
e = AB.randomIntAtoB(1,gridsize-3);
t = AB.randomIntAtoB(1,gridsize-3);
}
while(occupied(e,t)); // search for empty square
ei = e,
ej = t,
o = new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize),
(theenemy = new THREE.Mesh(o)).material = new THREE.MeshBasicMaterial
({
map:enemy_texture}),ABWorld.scene.add(theenemy),drawEnemy();
// set up agent
// start in random location
do
{
e = AB.randomIntAtoB(1,gridsize-2),
t = AB.randomIntAtoB(1,gridsize-2
)}
while(occupied(e,t)); // search for empty square
ai = e,
aj = t,
o = new THREE.BoxGeometry(squaresize,BOXHEIGHT,squaresize),
(theagent = new THREE.Mesh(o)).material = new THREE.MeshBasicMaterial({map:agent_texture}),
ABWorld.scene.add(theagent),
drawAgent(),
ABWorld.scene.background = (new THREE.CubeTextureLoader).load(SKYBOX_ARRAY,
function(){ABWorld.render(),AB.removeLoading(),AB.runReady = true;
})}
// --- draw moving objects -----------------------------------
// given ei, ej, draw it
function drawEnemy()
{
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 heuristic(e,t)
{
return diagonal?Math.hypot(e.i-t.i,e.j-t.j):Math.abs(e.i-t.i)+Math.abs(e.j-t.j);
}
function removeFromArray(e,t)
{
for
(
var
o = e.length-1;
o>=0;
o--)e[o]==t&&e.splice(o,1);
}
function Spot(e,t,o)
{
this.wall=o,
this.i=e,
this.j=t,
this.f=0,
this.g=0,
this.h=0,
this.neighbors=[],
this.previous=void 0;
}
function moveLogicalEnemy() // this is called by the infrastructure that gets action a from the Mind
{
for
(
var
e = 0;
e < gridsize; e++)
for
(
var
t = 0;
t < gridsize; t++)
{
var
o = GRID[e][t].wall;
GRID[e][t] = new Spot(e,t,o);
}
for
(
e = 0;
e < gridsize; e++)
for
(
t = 0;
t < gridsize;
t ++ )GRID.addNeighbors(e,t);
for
(
openSet = [],
closedSet = [],
start = GRID[ei][ej],
end = GRID[ai][aj],
openSet.push(start);
openSet.length > 0;
)
{
var a = 0;
for (
e = 0;
e < openSet.length; e++)
openSet[e].f<openSet[a].f&&(a = e);
var i = openSet[a];
if(i === end){path = [];
for(var n = i;
n.previous&&n.previous.previous;)path.push(n),
n = n.previous;
path.length>=1&&(ei == ai&&ej == aj||(ei = n.i,ej = n.j)),
console.log("success - found path")}removeFromArray(openSet,i),closedSet.push(i);
var r = GRID[i.i][i.j].neighbors;
for(e = 0;
e < r.length; e++)
{
var s=r[e];
if(!closedSet.includes(s)&&!s.wall)
{
var d = i.g+heuristic(s,i),u = !1;
openSet.includes(s)?d<s.g&&(s.g = d,u = !0):(s.g = d,u = !0,
openSet.push(s)),u&&(s.h=heuristic(s,end),s.f = s.g+s.h,s.previous = i);
}}}}
function moveLogicalAgent(e)
{
var t = ai,
o = aj;
e == ACTION_LEFT?t--:
e == ACTION_RIGHT?t++: e == ACTION_UP?o++:
e == ACTION_DOWN&&o--, occupied(t,o)||(ai = t,aj = o);
}
GRID.addNeighbors = function
(e,t)
{
e < gridsize-1&&GRID[e][t].neighbors.push(GRID[e+1][t]),
e > 0&&GRID[e][t].neighbors.push(GRID[e-1][t]),
t < gridsize-1&&GRID[e][t].neighbors.push(GRID[e][t+1]),
t > 0&&GRID[e][t].neighbors.push(GRID[e][t-1]),
diagonal&&(e>0&&t>0&&GRID[e][t].neighbors.push(GRID[e-1][t-1]),
e < gridsize-1&&t>0&&GRID[e][t].neighbors.push(GRID[e+1][t-1]),
e > 0&&t<gridsize-1&&GRID[e][t].neighbors.push(GRID[e-1][t+1]),
e < gridsize-1&&t<gridsize-1&&GRID[e][t].neighbors.push(GRID[e+1][t+1]))};
var
OURKEYS = [37,38,39,40];
function ourKeys(e)
{
return OURKEYS.includes(e.keyCode);
}
function keyHandler(e)
{
return!AB.runReady||(!ourKeys(e)||(37==e.keyCode&&moveLogicalAgent(ACTION_LEFT),
38 == e.keyCode&&moveLogicalAgent(ACTION_DOWN),
39 == e.keyCode&&moveLogicalAgent(ACTION_RIGHT),
40 == e.keyCode&&moveLogicalAgent(ACTION_UP),
e.stopPropagation(),e.preventDefault(),!1));
}
// --- score: -----------------------------------
function badstep() // is the enemy within one square of the agent
{
return Math.abs(ei-ai)<2&&Math.abs(ej-aj);
}
function agentBlocked() // agent is blocked on all sides, run over
{
return occupied(ai-1,aj)&&occupied(ai+1,aj)&&occupied(ai,aj+1)&&occupied(ai,aj-1);
}
function updateStatusBefore(e)
// this is called before anyone has moved on this step, agent has just proposed an action
// update status to show old state and proposed move
{
var t = AB.world.getState();
AB.msg(" Step: "+AB.step+" x = ("+t.toString()+") a = ("+e+") ");
}
function updateStatusAfter() // agent and enemy have moved, calculate score
{
// new state after both have moved
var e = AB.world.getState(),
t = goodsteps / AB.step * 100;
AB.msg(" y = ("+e.toString()+") <br> Bad steps: "+badsteps+" Good steps: "+goodsteps+" Score: "+t.toFixed(2)+"% ",2);
}
//--- public functions / interface / API ----------------------------------
AB.world.newRun=function()
{
AB.loadingScreen(),
AB.runReady = !1;
badsteps = 0;
goodsteps = 0 ;
BOXHEIGHT=squaresize;
ABWorld.init3d(startRadiusConst,maxRadiusConst,14548957);
loadResources(), // aynch file loads
// calls initScene() when it returns
document.onkeydown = keyHandler},
AB.world.getState = function()
{
return[ai,aj,ei,ej]},
AB.world.takeAction = function(e)
{
updateStatusBefore(e), // show status line before moves
moveLogicalAgent(e),
AB.step%2==0&&moveLogicalEnemy(), // slow the enemy down to every nth step
badstep()?badsteps++:goodsteps++,
drawAgent(),
drawEnemy(),
updateStatusAfter(); // show status line after moves
agentBlocked() // if agent blocked in, run over
&&(AB.abortRun = !0,
goodsteps = 0, // you score zero as far as database is concerned
musicPause(),
soundAlarm()
)},
AB.world.endRun = function()
{
musicPause(),
AB.abortRun?
AB.msg(" <br> <font color=red> <B> Agent trapped. Final score zero. </B> </font> ",3):
AB.msg(" <br> <font color=green> <B> Run over. </B> </font> ",3);
},
AB.world.getScore=function()
{
// only called at end - do not use ABRun.step because it may have just
var e = goodsteps/AB.maxSteps * 100; // float like 93.4372778
return Math.round(100 * e) / 100};
// --- music and sound effects --
var backmusic=AB.backgroundMusic(MUSIC_BACK);
function musicPlay(){backmusic.play();
}
function musicPause(){backmusic.pause();
}
function soundAlarm()
{
new Audio(SOUND_ALARM).play(); // play once, no loop
}
//--- end of while ------------------------------------------
if ( ! found ){
console.log ("No path found");
} else {
console.log ("found goal node");
highlightPath (person);
}