//THIS IS THE WORLD TO BE GRADED//I'd also like to cheekily mention that I was the first person to create a world so anyone who did anything similar is a copycat ;P//A list of some of the stuff I've implemented:// -lighting// -shadows(this was actually pretty hard, took me 2 or 3 days)// -turned the 2d grid structure 3d so you can place blocks on top of eachother// -implemented a scoring system based on par// -TRIED to implement pathfinding to get a more dynamic par and also make sure there's always a path from the ball to the hole// -I didn't get this finished but I commented out the code to look at// -Implemented a more interesting wall generation so that you don't just get blocks all over the place// -you can hit the ball at various strengths when you're beside it// -the ball bounces off surfaces// -the level is regenerated when you pot the ball// -fleshman//WHY THIS IS AN INTERESTING PROBLEM FOR AN AI TO SOLVE:// -an AI must first calculate a path from the ball to the hole// -then the AI must calculate a path from itself to the appropriate side of the ball // -the most simple AI will just putt the ball one square at a time to the hole, thus scoring exactly par (if i calculated the par correctly)// -a more complex AI would take distance into account and use varying strength to use less hits// -the best AI would take the 'bounce' effect into account, bouncing the ball off walls to line it up perfectly//as you can see there are varying degrees of competence for the AI, the more challenging it is to implement, the more impressive the final product seems//this, essentially, is what I think makes for an interesting problem for an AI to solve.// World must define these:const CLOCKTICK =75;// speed of run - move things every n millisecondsconst MAXSTEPS =10000;// length of a run before final scoreconst SCREENSHOT_STEP =50;//---- global constants: -------------------------------------------------------const gridsize =20;// number of squares along side of world const NOBOXES =Math.trunc (((gridsize*gridsize)/100)*7);//around 7% of the level will be walls, seems to give favourable results const squaresize =100;// size of square in pixelsconst 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 atconst skyboxConst = MAXPOS *100;// where to put skybox const maxRadiusConst = MAXPOS *100;// maximum distance from camera we will render things const show3d =true;// Switch between 3d and 2d view (both using Three.js) //--- 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;const ACTION_PUTT =5;//hit the ball one squareconst ACTION_SHOT =6;//3 squaresconst ACTION_DRIVE =7;//6// 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){returnfalse;}else{returntrue;}}//---- start of World class -------------------------------------------------------functionWorld(){// most of World can be private // regular "var" syntax means private variables:var GRID =newArray(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 3D array var WALLS =newArray(4* gridsize );// need to keep handle to each wall block object so can find it later to paint it var BASE =newArray(gridsize);var MAZE =newArray( NOBOXES );var theagent, theball;var theagentmodel;var goalModel;var ballMoves =0;var ballDir ="up";var score =0;var par =0;var hits =0;// contents of a grid squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;const GRID_BASE =3;const GRID_GOAL =4;// enemy and agent position on squaresvar 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]=newArray(gridsize);// each element is an array for(var j =0; j < gridsize ; j++){
GRID[i][j]=newArray(gridsize);for(var k =0; k < gridsize; k++){
GRID[i][j][k]= GRID_BLANK;}}}}function occupied ( i, j )// is this square occupied{if(( ei == i )&&( ej == j ))returntrue;// variable objects //if ( ( ai == i ) && ( aj == j ) ) return true;if( GRID[i][j][1]== GRID_WALL )returntrue;// fixed objectif( GRID[i][j][1]== GRID_MAZE )returntrue;returnfalse;}// 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));}//--- skybox ----------------------------------------------------------------------------------------------function initSkybox(){// x,y,z positive and negative faces have to be in certain order in the array var materialArray =[(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0001.bmp"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0004.bmp"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0003.bmp"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0006.bmp"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0005.bmp"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/skyrender0002.bmp"), side: THREE.BackSide}))];var skyGeometry =new THREE.CubeGeometry( skyboxConst, skyboxConst, skyboxConst );var skyMaterial =new THREE.MeshFaceMaterial( materialArray );var theskybox =new THREE.Mesh( skyGeometry, skyMaterial );
threeworld.scene.add( theskybox );// We are inside a giant Cube}// This does the file read the old way using loadTexture.// (todo) Change to asynchronous TextureLoader. A bit complex:// Make blank skybox. Start 6 asynch file loads to call 6 return functions.// Each return function checks if all 6 loaded yet. Once all 6 loaded, paint the skybox.// --- asynch load textures from file ----------------------------------------// credits:// http://commons.wikimedia.org/wiki/File:Old_door_handles.jpg?uselang=en-gb// https://commons.wikimedia.org/wiki/Category:Pac-Man_icons// https://commons.wikimedia.org/wiki/Category:Skull_and_crossbone_icons// loader return can call private functionfunction loadTextures(){var manager =new THREE.LoadingManager();var oloader =new THREE.OBJLoader( manager );
oloader.load("/uploads/seanhutchinson/Sporty_Man.obj", buildagent );
oloader.load("/uploads/seanhutchinson/usmcflag.obj", buildGoal );//ORIGINALLY USED MESHBASICMATERIAL BUT I CHANGED IT TO MESHLAMBERTMATERIAL BECAUSE THAT TAKES LIGHT AND SHADOWSvar loader1 =new THREE.TextureLoader();
loader1.load ('/uploads/seanhutchinson/Brick-2378.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintWalls (new THREE.MeshLambertMaterial({ map: thetexture }));});var loader2 =new THREE.TextureLoader();
loader2.load ('/uploads/starter/pacman.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
theagent.material =new THREE.MeshLambertMaterial({ map: thetexture });});var loader3 =new THREE.TextureLoader();
loader3.load ('/uploads/seanhutchinson/golf3.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
theball.material =new THREE.MeshLambertMaterial({ map: thetexture });});var loader4 =new THREE.TextureLoader();
loader4.load ('/uploads/seanhutchinson/grass.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintBaseBlocks (new THREE.MeshLambertMaterial({ map: thetexture }));});
loadMazeTexture();var loader6 =new THREE.TextureLoader();
loader6.load ('/uploads/seanhutchinson/goal.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintGoal (new THREE.MeshLambertMaterial({ map: thetexture }));});}function loadMazeTexture()//we create this seperately so we can reset the level without messing with everything else{var loader =new THREE.TextureLoader();
loader.load ('/uploads/seanhutchinson/Brick-2378.jpg',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintMaze (new THREE.MeshLambertMaterial({ map: thetexture }));});}function buildagent ( object ){
object.name ="agentmodel";
object.traverse( paintAgent );
object.scale.set(100,100,100);
object.position.z = theagent.position.z;
object.position.y = theagent.position.y -(squaresize/2);
object.position.x = theagent.position.x;
theagentmodel = object;
threeworld.scene.add( theagentmodel );}function buildGoal ( object ){
object.name ="goalModel";
object.traverse( paintGoalModel );
object.scale.set(2,2,2);
object.rotation.y =Math.PI /4;
object.position.z = translate (2* squaresize );
object.position.y =25;
object.position.x = translate (2* squaresize );
goalModel = object;
threeworld.scene.add( goalModel );}function paintGoalModel ( child ){if( child instanceof THREE.Mesh){
child.material.map = THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/flag.jpg");//child.material.map.wrapS = child.material.map.wrapT = THREE.RepeatWrapping;//child.material.map.repeat.set( 32, 32 );
child.castShadow =true;
child.receiveShadow =true;}}function paintAgent ( child ){if( child instanceof THREE.Mesh){
child.material.map = THREE.ImageUtils.loadTexture("/uploads/seanhutchinson/flesh.jpg");
child.material.map.wrapS = child.material.map.wrapT = THREE.RepeatWrapping;
child.material.map.repeat.set(8,8);
child.castShadow =true;
child.receiveShadow =true;}}// --- add fixed objects ---------------------------------------- // my numbering is 0 to gridsize-1//HERE'S SOME MESSY CODE THAT GIVES US RANDOM WALLS INSTEAD OF JUST SINGULAR BLOCKS :D//IT'S STILL DOESN'T YIELD 'INTELLIGENT-LOOKING' RESULTS BUT IT'S MUCH BETTER THAN BEFOREfunction initLogicalMaze(){for(var c =0; c < NOBOXES ; c++){//this gives us our random position for the starter piecevar i = randomintAtoB(1,gridsize-1);// inner squares are 1 to gridsize-2 (NOTE: THIS IS AN ORIGINAL COMMENT BUT I THINK IT'S WRONG SO I CHANGED THE CODE)var j = randomintAtoB(1,gridsize-1);if(!(i == ai && j == aj)&&!(i == ei && j == ej)&&!(i ==2&& j ==2))//make sure the ball or player or goal isn't there{
GRID[i][j][1]= GRID_MAZE ;}var randLength = randomintAtoB(5,15);//walls should be a random length from 5 to 15var xORy = randomintAtoB(1,2);//this is a pretty bad way of deciding if we're moving in the x or y axisif(xORy ==1)//we're changing the x{for(var n = i; n < randLength; n++){if(n < gridsize && n >0)//we wouldn't want to put a block off the grid now would we?{if(!(n == ai && j == aj)&&!(n == ei && j == ej)&&!(n ==2&& j ==2))//make sure the ball or player or goal isn't there{
GRID[n][j][1]= GRID_MAZE ;}}}}else//we're changing the y{for(var n = j; n < randLength; n++){if(n < gridsize && n >0)//we wouldn't want to put a block off the grid now would we?{if(!(i == ai && n == aj)&&!(i == ei && n == ej)&&!(i ==2&& n ==2))//make sure the ball or player or goal isn't there{
GRID[i][n][1]= GRID_MAZE ;}}}}}}function initThreeMaze(){var t =0;for(var i =0; i < gridsize ; i++)for(var j =0; j < gridsize ; j++)if( GRID[i][j][1]== GRID_MAZE ){var shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );var thecube =new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate ( i * squaresize );
thecube.position.z = translate ( j * squaresize );
thecube.position.y = squaresize;
thecube.castShadow =true;
thecube.receiveShadow =true;
threeworld.scene.add(thecube);
MAZE[t]= thecube;// save it for later
t++;}}function paintMaze ( material ){for(var i =0; i < MAZE.length; i++){if( MAZE[i]) MAZE[i].material = material;}}function initLogicalWalls()// set up logical walls in data structure, whether doing graphical run or not {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][1]= GRID_WALL;// set up data structure, whether using Three.js or not}}function initThreeWalls()// graphical run only, 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][1]==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 = squaresize;
thecube.castShadow =true;
thecube.receiveShadow =true;
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;}}function initLogicalBaseBlocks(){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][0]= GRID_BASE;// set up data structure, whether using Three.js or not}}function initThreeBaseBlocks()// graphical run only, 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][0]== GRID_BASE ){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;//underneath all the other blocks
thecube.receiveShadow =true;
threeworld.scene.add(thecube);
BASE[t]= thecube;// save it for later
t++;}}function paintBaseBlocks ( material )// paint blank boxes {for(var i =0; i < BASE.length; i++){if( BASE[i]) BASE[i].material = material;}}function initLogicalGoal(){
GRID[2][2][0]= GRID_GOAL;}function initThreeGoal()// graphical run only, set up blank boxes, painted later {var shape =new THREE.BoxGeometry( squaresize, squaresize, squaresize );var thecube =new THREE.Mesh( shape );
thecube.material.color.setHex( BLANKCOLOR );
thecube.position.x = translate (2* squaresize );// translate my simple (i,j) block-numbering coordinates to three.js (x,y,z) coordinates
thecube.position.z = translate (2* squaresize );
thecube.position.y =0;//underneath all the other blocks
thecube.receiveShadow =true;
threeworld.scene.add(thecube);
BASE[gridsize*gridsize]= thecube;}function paintGoal ( material )// paint blank boxes {
BASE[gridsize*gridsize].material = material;}function clearMaze(){for(var i =1; i < gridsize-1; i++)for(var j =1; j < gridsize-1; j++)//if ( ( i==0 ) || ( i==gridsize-1 ) || ( j==0 ) || ( j==gridsize-1 ) ){
GRID[i][j][1]= GRID_BLANK;// set up data structure, whether using Three.js or not}for(var i =0; i < MAZE.length; i++){
threeworld.scene.remove( MAZE[i]);}}function resetLevel(){
clearMaze();
ai = gridsize-2;
aj = gridsize-2;
ei = gridsize-3;
ej = gridsize-3;
hits =0;
initLogicalMaze();if(true){
initThreeMaze();
loadMazeTexture();}}function initThreeLights(){//this acts like a sunvar dirLight =new THREE.DirectionalLight(0xffffff,1);//white light
dirLight.position.set((gridsize*squaresize)*2,(gridsize*squaresize)*3,(gridsize*squaresize));//this will put our light in the bottom corner
dirLight.CameraLightHelper=true;
threeworld.scene.add(dirLight);//give us some light everywhere to make sure we have no 100% blackvar ambiLight =new THREE.AmbientLight(0x1a1a1a);
threeworld.scene.add(ambiLight);//THIS GIVES US BEAUTIFUL SHADOWS//SHADOWS ARE A BIT PIXELATED BUT THIS IS AS GOOD AS YOU'LL GET WITH JUST THE ONE LIGHT COVERING EVERYTHINGvar spotLight =new THREE.SpotLight(0xffffff,0,(gridsize*squaresize)*3,75,10);
spotLight.position.set((gridsize*squaresize),(gridsize*squaresize),(gridsize*squaresize));
spotLight.castShadow =true;var spotTarget =new THREE.Object3D();
threeworld.scene.add(spotLight);
spotTarget.position.set(0,0,0);
spotLight.target = spotTarget;
threeworld.scene.add(spotLight);}// --- BALL functions -----------------------------------function drawBall()// given ei, ej, draw it {var x = translate ( ei * squaresize );var z = translate ( ej * squaresize );var y =(5*squaresize)/8;
theball.position.x = x;
theball.position.y = y;
theball.position.z = z;
threeworld.scene.add(theball);
threeworld.lookat.copy ( theball.position );// if camera moving, look back at where the enemy is }function initLogicalBall(){// start at same place every time:
ei = gridsize -3;// this square will be free
ej = gridsize -3;// (bug) use Math.trunc or else you get a bad square number if gridsize is odd}function initThreeBall(){var shape =new THREE.SphereGeometry( squaresize/8, squaresize/8, squaresize/8);
theball =new THREE.Mesh( shape );
theball.material.color.setHex( BLANKCOLOR );
theball.castShadow =true;
theball.receiveShadow =false;//it can be hard to see in shadow
drawBall();}function moveLogicalBall(){// small random movevar i = ei;var j = ej;if(ballDir =="right"){
i = ei+1;}if(ballDir =="left"){
i = ei-1;}if(ballDir =="down"){
j = ej+1;}if(ballDir =="up"){
j = ej-1;}if(ballMoves >0){if(! occupied(i,j))// if no obstacle then move, else just miss a turn{//ROTATE THE BALL SO IT LOOKS LIKE IT'S ROLLINGif( ei < i)//move right{
theball.rotation.z -=0.75;}elseif(ei > i)//move left{
theball.rotation.z +=0.75;}elseif( ej < j)//move down{
theball.rotation.x +=0.75;}elseif(ej > j)//move up{
theball.rotation.x -=0.75;}
ei = i;
ej = j;
ballMoves--;}else//if you've hit a wall{if(ballDir =="right"){
ballDir ="left";}elseif(ballDir =="left"){
ballDir ="right";}elseif(ballDir =="down"){
ballDir ="up";}elseif(ballDir =="up"){
ballDir ="down";}}}elseif((ei ==2)&&(ej ==2))//if you're not moving AND you're on the goal{//the ball disappears (an illusion that it's been sunk)
ei =0;
ej =0;
score += self.getScore();
resetLevel()}}// --- agent functions -----------------------------------function drawAgent()// given ai, aj, draw it {var x = translate ( ai * squaresize );var z = translate ( aj * squaresize );var y = squaresize;
theagent.position.x = x;
theagent.position.y = y;
theagent.position.z = z;//threeworld.scene.add(theagent);if( theagentmodel ){
theagentmodel.position.x = x;
theagentmodel.position.y = y -(squaresize/2);
theagentmodel.position.z = z;
theagentmodel.rotation.y = theagent.rotation.y;}
threeworld.follow.copy ( theagent.position );// follow vector = agent position (for camera following agent)}function initLogicalAgent(){// start at same place every time:
ai = gridsize -2;// this square will be free
aj = gridsize -2;}function initThreeAgent(){var shape =new THREE.BoxGeometry( squaresize, squaresize, squaresize );
theagent =new THREE.Mesh( shape );
theagent.material.color.setHex( BLANKCOLOR );
theagent.castShadow =true;
theagent.receiveShadow =true;
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 ){
theagent.rotation.y =Math.PI /-2;
i--;}elseif( a == ACTION_RIGHT ){
theagent.rotation.y =Math.PI /2;
i++;}elseif( a == ACTION_UP ){
theagent.rotation.y =0;
j++;}elseif( a == ACTION_DOWN ){
theagent.rotation.y =Math.PI;
j--;}if(! occupied(i,j)){
ai = i;
aj = j;}if(a == ACTION_PUTT){//check if the ball is beside usif((ei == ai+1)&&(ej == aj))//ball is to our immediate right{
hits++;
ballMoves =1;
ballDir ="right";}if((ei == ai-1)&&(ej == aj))//ball is to our immediate left{
hits++;
ballMoves =1;
ballDir ="left";}if((ej == aj+1)&&(ei == ai))//ball is to our immediate underneath{
hits++;
ballMoves =1;
ballDir ="down";}if((ej == aj-1)&&(ei == ai))//ball is to our immediate above{
hits++;
ballMoves =1;
ballDir ="up";}}if(a == ACTION_SHOT){//check if the ball is beside usif((ei == ai+1)&&(ej == aj))//ball is to our immediate right{
hits++;
ballMoves =3;
ballDir ="right";}if((ei == ai-1)&&(ej == aj))//ball is to our immediate left{
hits++;
ballMoves =3;
ballDir ="left";}if((ej == aj+1)&&(ei == ai))//ball is to our immediate underneath{
hits++;
ballMoves =3;
ballDir ="down";}if((ej == aj-1)&&(ei == ai))//ball is to our immediate above{
hits++;
ballMoves =3;
ballDir ="up";}}if(a == ACTION_DRIVE){//check if the ball is beside usif((ei == ai+1)&&(ej == aj))//ball is to our immediate right{
hits++;
ballMoves =6;
ballDir ="right";}if((ei == ai-1)&&(ej == aj))//ball is to our immediate left{
hits++;
ballMoves =6;
ballDir ="left";}if((ej == aj+1)&&(ei == ai))//ball is to our immediate underneath{
hits++;
ballMoves =6;
ballDir ="down";}if((ej == aj-1)&&(ei == ai))//ball is to our immediate above{
hits++;
ballMoves =6;
ballDir ="up";}}}// --- score and pathfinding: -----------------------------------/*
function pathFinding(startX,startY,goalX,goalY)
{
//START AT STARTX, STARTY
//SEARCH QUEUE
//FIRST TAKE THE NODE OFF THE QUEUE
//THEN VISITED = TRUE
//IF THE NODE IS THE GOAL THEN BREAK
//ELSE CHECK IF ALL ADJACENT NODES ARE VIABLE
//IF THEY'RE NOT VISITED, THEY ARE NOW
//ADD YOUR CURRENT NODE'S PATH ARRAY + THE NEW NODE (append) TO EACH THEM
//ADD THEM TO THE QUEUE
//NOW WE CAN JUST CHECK THE PATH OF THE GOAL NODE
var nodeList = [];
var openList = [];
var closedList = [];
//initialise our openList with all the spaces on the map
for(var i = 0; i < NOBOXES; i++)
{
for(var j = 0; j < NOBOXES; j++)
{
var node;
var node.x = i;
node.y = j;
node.path = [];
node.distance = distance(x, y, goalX, goalY); //this will be our 'h value'
node.moveCost = path.length; //this will be our 'g value'
node.fVal = distance + moveCost;
//x: i,
//y: j,
//path: [],
//distance: distance(x, y, goalX, goalY), //this will be our 'h value'
//moveCost: path.length, //this will be our 'g value'
//fVal: distance + moveCost
nodeList.push(node);
}
}
//find our start node
for (var i = 0; i < nodeList.length; i++)
{
if(nodeList[i].x == startX && nodeList[i].y == startY)
{
openList.push(nodeList[i]);
break;
}
}
while(openList.length > 0)
{
var i = smallestFVal(openList);
}
}
*/function distance(x1,y1,x2,y2){//'MANHATTAN' DISTANCEreturnMath.abs(x1-x2)+Math.abs(y1-y2);}function smallestFVal(list){var smallestIndex =0;var smallest = list[0].fVal;for(var i =0; i < list.length; i++){if(list[i].fVal < smallest){
smallest = list[i].fVal;
smallestIndex = i;}}return smallestIndex;}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 status =" Par: "+ par +" Hits: "+ hits +" Score: "+ score;
$("#user_span1").html( status );}//--- public functions / interface / API ----------------------------------------------------------// must have this public variable:this.endCondition;// If set to true, run will end. this.keyHandler =function(e){if(e.keyCode ==37)// left{
moveLogicalAgent( ACTION_LEFT );}if(e.keyCode ==39)// right{
moveLogicalAgent( ACTION_RIGHT );}if(e.keyCode ==38)// down{
moveLogicalAgent( ACTION_DOWN );}if(e.keyCode ==40)// up{
moveLogicalAgent( ACTION_UP );}if(e.keyCode ==97)// numpad1 is putt{
moveLogicalAgent( ACTION_PUTT );}if(e.keyCode ==98)// numpad2 is shot{
moveLogicalAgent( ACTION_SHOT );}if(e.keyCode ==99)// numpad3 is drive{
moveLogicalAgent( ACTION_DRIVE );}};this.newRun =function(){
document.onkeydown =this.keyHandler;// (subtle bug) must reset variables like these inside newRun (in case do multiple runs)this.endCondition =false;
badsteps =0;
goodsteps =0;
step =0;// define logical data structure for the World, even if no graphical representation:
initGrid();
initLogicalWalls();
initLogicalBaseBlocks();
initLogicalAgent();
initLogicalBall();
initLogicalMaze();
initLogicalGoal();
par = distance(ai,aj,2,2);// if Three.js graphical representation:if(true){if( show3d ){
BOXHEIGHT = squaresize;
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}else{
BOXHEIGHT =1;
threeworld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}
initSkybox();// Set up blank objects first:
threeworld.renderer.shadowMap.enabled =true;
threeworld.renderer.shadowMapSoft =false;//soft shadows is nice in theory but their anti-aliasing is shite so don't bother//console.log(threeworld.renderer);
initThreeLights();
initThreeWalls();
initThreeBaseBlocks();
initThreeMaze();
initThreeAgent();
initThreeBall();
initThreeGoal();// 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.nextStep =function(){var a =4;
step++;
moveLogicalAgent(a);
moveLogicalBall();if( badstep())
badsteps++;else
goodsteps++;if(true){
drawAgent();
drawBall();
updateStatus();}};this.endRun =function(){};this.getScore =function(){return(par - hits);};}//---- end of World class -------------------------------------------------------