// ==== 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.// ====================================================================================================================// ==========================================================================// Simple starter World // 3d-effect World (really a 2-D problem)//// Hero = agent = pacman. // Enemy moves randomly.// Movement is on an invisible grid of squares //// This simple World shows:// - Texture load from files (asynchronous file reads)// - Write status to <span> in run window//// It also shows functionality that is built-in to every World:// - Camera control buttons // - Pause/step run// ==========================================================================// =============================================================================================// Scoring:// Bad steps = steps where enemy is within one step of agent.// Good steps = steps where enemy is further away. // Score = good steps.// =============================================================================================// ===================================================================================================================// === 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 TEXTURE_WALL ='/uploads/starter/door.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_iconsconst gridsize =15;// number of squares along side of world const squaresize =100;// size of square in pixelsconst SKYCOLOR =0xffffcc;// a number, not a string const MAXPOS = gridsize * squaresize;// length of one side in pixels const startRadiusConst = MAXPOS *0.8;// distance from centre to start the camera atconst maxRadiusConst = MAXPOS *3;// maximum distance from camera we will render things //--- change ABWorld defaults: ----------------------------------------------ABHandler.GROUNDZERO =true;// "ground" exists at altitude zero// ===================================================================================================================// === 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)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;// enemy and agent position on squaresvar 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();
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();});}function asynchFinished(){if( wall_texture && agent_texture && enemy_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]==true)returntrue;// fixed objectreturnfalse;}// 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 array// GRID = new Array(gridsize);// now make each element an array for( 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]=true;
shape =new THREE.BoxGeometry( squaresize, squaresize, 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]=false;// set up enemy // start at same place every time:
ei =Math.trunc ( gridsize /2);// this square will be free
ej =Math.trunc ( gridsize /2);// (bug) use Math.trunc or else you get a bad square number if gridsize is odd
shape =new THREE.BoxGeometry( squaresize, squaresize, squaresize );
theenemy =new THREE.Mesh( shape );
theenemy.material =new THREE.MeshBasicMaterial({ map: enemy_texture });ABWorld.scene.add(theenemy);
drawEnemy();// set up agent // start at same place every time:
ai =Math.trunc ( gridsize /2)+1;// this square will be free
aj =Math.trunc ( gridsize /2)+1;
shape =new THREE.BoxGeometry( squaresize, squaresize, squaresize );
theagent =new THREE.Mesh( shape );
theagent.material =new THREE.MeshBasicMaterial({ map: agent_texture });ABWorld.scene.add(theagent);
drawAgent();// can start the run loopABWorld.render();
AB.runReady =true;}// --- draw moving objects -----------------------------------function drawEnemy()// given ei, ej, draw it {
theenemy.position.copy ( translate(ei,ej));// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates ABWorld.lookat.copy ( theenemy.position );// if camera moving, look back at where the enemy is }function drawAgent()// given ai, aj, draw it {
theagent.position.copy ( translate(ai,aj));// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates ABWorld.follow.copy ( theagent.position );// follow vector = agent position (for camera following agent)}// --- take actions -----------------------------------function moveLogicalEnemy(){// small random movevar i = AB.randomIntAtoB ( ei-1, ei+1);var j = AB.randomIntAtoB ( ej-1, ej+1);if(! occupied(i,j))// if no obstacle then move, else just miss a turn{
ei = i;
ej = j;}}function moveLogicalAgent( a )// this is called by the infrastructure that gets action a from the Mind {var i = ai;var j = aj;if( a == ACTION_LEFT ) i--;elseif( a == ACTION_RIGHT ) i++;elseif( a == ACTION_UP ) j++;elseif( a == ACTION_DOWN ) j--;if(! occupied(i,j)){
ai = i;
aj = j;}}// --- score: -----------------------------------function badstep()// is the enemy within one square of the agent{if((Math.abs(ei - ai)<2)&&(Math.abs(ej - aj)<2))returntrue;elsereturnfalse;}function updateStatus(){var score = AB.world.getScore();
AB.msg (" Step: "+ AB.step +" out of "+ AB.maxSteps +". Score: "+ score );}
AB.world.newRun =function(){
AB.runReady =false;
badsteps =0;
goodsteps =0;ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );
loadResources();// aynch file loads // calls initScene() when it returns };
AB.world.getState =function(){var x =[ ai, aj, ei, ej ];return( x );};
AB.world.takeAction =function( a ){
moveLogicalAgent(a);
moveLogicalEnemy();if( badstep()) badsteps++;else goodsteps++;
drawAgent();
drawEnemy();
updateStatus();};
AB.world.getScore =function(){return goodsteps;};