// Cloned by SinfulSalad on 29 Jun 2018 from World "Simple World" by Starter user // Please leave this clone trail here. // ==== Starter World =============================================================================================== // (c) Ancient Brain Ltd. All rights reserved. // This code is only for use on the Ancient Brain site. // This code may be freely copied and edited by anyone on the Ancient Brain site. // This code may not be copied, re-published or used on any other website. // To include a run of this code on another website, see the "Embed code" links provided on the Ancient Brain site. // ================================================================================================================== // ========================================================================== // Simple starter World // 3d-effect World (really a 2-D problem) // // Hero = agent = pacman. // Enemy moves randomly. // // 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_icons const gridsize = 15; // number of squares along side of world const squaresize = 100; // size of square in pixels const MAXPOS = gridsize * squaresize; // length of one side in pixels const SKYCOLOR = 0xffffcc; // a number, not a string const BLANKCOLOR = SKYCOLOR ; // make objects this color until texture arrives (from asynchronous file read) const startRadiusConst = MAXPOS * 0.8 ; // distance from centre to start the camera at const maxRadiusConst = MAXPOS * 3 ; // maximum distance from camera we will render things //--- change threeworld defaults: ---------------------------------------------- threehandler.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) // --- some useful random functions ------------------------------------------- function randomfloatAtoB ( A, B ) { return ( A + ( Math.random() * (B-A) ) ); } function randomintAtoB ( A, B ) { return ( Math.round ( randomfloatAtoB ( A, B ) ) ); } function randomBoolean() { if ( Math.random() < 0.5 ) { return false; } else { return true; } } //---- start of World class ------------------------------------------------------- function World() { // most of World can be private // regular "var" syntax means private variables: var GRID = new Array(gridsize); // can query GRID about whether squares are occupied, will in fact be initialised as a 2D array var WALLS = new Array ( 4 * gridsize ); // need to keep handle to each wall block object so can find it later to paint it var theagent, theenemy; // enemy and agent position on squares var ei, ej, ai, aj; var badsteps; var goodsteps; var step; var self = this; // needed for private fn to call public fn - see below // regular "function" syntax means private functions: function initGrid() { for (var i = 0; i < gridsize ; i++) { GRID[i] = new Array(gridsize); // each element is an array for (var j = 0; j < gridsize ; j++) { GRID[i][j] = false; } } } function occupied ( i, j ) // is this square occupied { if ( ( ei == i ) && ( ej == j ) ) return true; // variable objects if ( ( ai == i ) && ( aj == j ) ) return true; if ( GRID[i][j] == true ) return true; // fixed object return false; } // 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 ( x ) { return ( x - ( MAXPOS/2 ) ); } // --- asynch load textures from file ---------------------------------------- // loader return can call private function function loadTextures() { var loader1 = new THREE.TextureLoader(); loader1.load ( TEXTURE_WALL, function ( thetexture ) { thetexture.minFilter = THREE.LinearFilter; paintWalls ( new THREE.MeshBasicMaterial( { map: thetexture } ) ); } ); var loader2 = new THREE.TextureLoader(); loader2.load ( TEXTURE_AGENT, function ( thetexture ) { thetexture.minFilter = THREE.LinearFilter; theagent.material = new THREE.MeshBasicMaterial( { map: thetexture } ); } ); var loader3 = new THREE.TextureLoader(); loader3.load ( TEXTURE_ENEMY, function ( thetexture ) { thetexture.minFilter = THREE.LinearFilter; theenemy.material = new THREE.MeshBasicMaterial( { map: thetexture } ); } ); } // --- add fixed objects ---------------------------------------- // my numbering is 0 to gridsize-1 function initLogicalWalls() // set up logical walls in data structure { for (var i = 0; i < gridsize ; i++) for (var j = 0; j < gridsize ; j++) if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) ) { GRID[i][j] = true; // set up data structure } } function initThreeWalls() // set up blank boxes, painted later { var t = 0; for (var i = 0; i < gridsize ; i++) for (var j = 0; j < gridsize ; j++) if ( GRID[i][j] == true ) { var shape = new THREE.BoxGeometry( squaresize, squaresize, squaresize ); var thecube = new THREE.Mesh( shape ); thecube.material.color.setHex( BLANKCOLOR ); thecube.position.x = translate ( i * squaresize ); // translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates thecube.position.z = translate ( j * squaresize ); thecube.position.y = 0; threeworld.scene.add(thecube); WALLS[t] = thecube; // save it for later t++; } } function paintWalls ( material ) // paint blank boxes { for ( var i = 0; i < WALLS.length; i++ ) { if ( WALLS[i] ) WALLS[i].material = material; } } // --- enemy functions ----------------------------------- function drawEnemy() // given ei, ej, draw it { var x = translate ( ei * squaresize ); var z = translate ( ej * squaresize ); var y = 0; theenemy.position.x = x; theenemy.position.y = y; theenemy.position.z = z; threeworld.scene.add(theenemy); threeworld.lookat.copy ( theenemy.position ); // if camera moving, look back at where the enemy is } function initLogicalEnemy() { // 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 } function initThreeEnemy() { var shape = new THREE.BoxGeometry( squaresize, squaresize, squaresize ); theenemy = new THREE.Mesh( shape ); theenemy.material.color.setHex( BLANKCOLOR ); drawEnemy(); } function moveLogicalEnemy() { // small random move var i = randomintAtoB ( ei-1, ei+1 ); var j = randomintAtoB ( ej-1, ej+1 ); if ( ! occupied(i,j) ) // if no obstacle then move, else just miss a turn { ei = i; ej = j; } } // --- agent functions ----------------------------------- function drawAgent() // given ai, aj, draw it { var x = translate ( ai * squaresize ); var z = translate ( aj * squaresize ); var y = 0; theagent.position.x = x; theagent.position.y = y; theagent.position.z = z; threeworld.scene.add(theagent); threeworld.follow.copy ( theagent.position ); // follow vector = agent position (for camera following agent) } function initLogicalAgent() { // start at same place every time: ai = Math.trunc ( gridsize / 2 ) + 1; // this square will be free aj = Math.trunc ( gridsize / 2 ) + 1; } function initThreeAgent() { var shape = new THREE.BoxGeometry( squaresize, squaresize, squaresize ); theagent = new THREE.Mesh( shape ); theagent.material.color.setHex( BLANKCOLOR ); drawAgent(); } 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; } } // --- 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 updateStatus() { var score = self.getScore(); var status = " Step: " + step + " out of " + AB.maxSteps + ". Score: " + score; $("#user_span1").html( status ); } //--- public functions / interface / API ---------------------------------------------------------- this.newRun = function() { badsteps = 0; goodsteps = 0; step = 0; // define logical data structure for the World: initGrid(); initLogicalWalls(); initLogicalAgent(); initLogicalEnemy(); threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR ); // Set up blank objects first: initThreeWalls(); initThreeAgent(); initThreeEnemy(); // Then paint them with textures - asynchronous load of textures from files. // The texture file loads return at some unknown future time in some unknown order. // Because of the unknown order, it is probably best to make objects first and later paint them, rather than have the objects made when the file reads return. // It is safe to paint objects in random order, but might not be safe to create objects in random order. loadTextures(); // will return sometime later, but can go ahead and render now }; this.getState = function() { var x = [ ai, aj, ei, ej ]; return ( x ); }; this.takeAction = function ( a ) { step++; moveLogicalAgent(a); moveLogicalEnemy(); if ( badstep() ) badsteps++; else goodsteps++; drawAgent(); drawEnemy(); updateStatus(); }; this.getScore = function() { return goodsteps; }; } //---- end of World class -------------------------------------------------------