// Cloned by Teacher World samples on 10 Dec 2017 from World "Sheepdog3(Submission)" by Neil Geoghegan // Please leave this clone trail here./*
KNOWN BUGS
-----------
dog will pin sheep against wall forever
dog will get stuck at a wall if sheep is on the other side of it
gate sometimes closes on the sheep
*/const CLOCKTICK =150;// speed of run - move things every n millisecondsconst MAXSTEPS =1000;// length of a run before final score//---- global constants: -------------------------------------------------------const gridsize =20;// number of squares along side of worldconst squaresize =100;// size of square in pixelsconst MAXPOS = gridsize * squaresize;// length of one side in pixelsconst NOBOXES =Math.trunc ((gridsize * gridsize)/15);const SKYCOLOR =0xffffcc;// a number, not a stringconst BLANKCOLOR = SKYCOLOR ;// make objects this color until texture arrives (from asynchronous file read)const LIGHTCOLOR =0xffffff;const startRadiusConst = MAXPOS *0.8;// distance from centre to start the camera atconst skyboxConst = MAXPOS *3;// where to put skyboxconst maxRadiusConst = MAXPOS *5;// maximum distance from camera we will render things//--- 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;// contents of a grid squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;// --- 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;}}function randomPick ( a, b ){if( randomBoolean())return a;elsereturn b;}//---- start of World class -------------------------------------------------------functionWorld(){//---My functions------------------------------------------------------------------function looseSheep(){if(zone(e1i,e1j)!=2)returntrue;if(zone(e2i,e2j)!=2)returntrue;if(zone(e3i,e3j)!=2)returntrue;returnfalse;}function sheepCaught(){var count =0;if(zone(e1i,e1j)==2) count++;if(zone(e2i,e2j)==2) count++;if(zone(e3i,e3j)==2) count++;return count;}//raise and lower the gate logicallyfunction liftGate(){for(var i =6; i <=10; i++){
MAZE[i].position.y =(MAZE[i].position.y)+100;}for(var i =7; i <12; i++){
GRID[i][6]= GRID_BLANK ;}
gateLifted =true;}function lowerGate(){for(var i =6; i <=10; i++){
MAZE[i].position.y =(MAZE[i].position.y)-100;}for(var i =7; i <12; i++){
GRID[i][6]= GRID_MAZE;}
gateLifted =false;}//determine what zone you are curently in.function zone(i,j){if((i =>1)&&(i <=6)&&(j =>1)&&(j <=6))return1if((i >6)&&(i <=12)&&(j =>1)&&(j <=6))return2if((i >12)&&(i <=18)&&(j =>1)&&(j <=6))return3if((i =>1)&&(i <=7)&&(j >6)&&(j <=12))return4if((i >7)&&(i <=11)&&(j >6)&&(j <=12))return5if((i >=12)&&(i <=18)&&(j >6)&&(j <=12))return6if((i =>1)&&(i <=7)&&(j >12)&&(j <=18))return7if((i >7)&&(i <=11)&&(j >12)&&(j <=18))return8if((i >=12)&&(i <=18)&&(j >12)&&(j <=18))return9}var sheepCount =0;var GRID =newArray(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 2D arrayvar WALLS =newArray(4* gridsize );// need to keep handle to each wall block object so can find it later to paint itvar MAZE =newArray( NOBOXES );var theagent, theenemy1, theenemy2, theenemy3;var agentRotation =0;var enemy1Rotation =0;// with 3D models, current rotation away from default orientationvar enemy2Rotation =0;var enemy3Rotation =0;// enemy and agent position on squaresvar ai, aj;var e1i, e1j;var e2i, e2j;var e3i, e3j;var gateLifted =false;//determine if gate is up/downvar badsteps;var goodsteps;var step;var self =this;// needed for private fn to call public fn - see belowfunction initGrid(){for(var i =0; i < gridsize ; i++){
GRID[i]=newArray(gridsize);// each element is an arrayfor(var j =0; j < gridsize ; j++){
GRID[i][j]= GRID_BLANK ;}}}function occupied ( i, j )// is this square occupied{if(( e1i == i )&&( e1j == j ))returntrue;// variable objectsif(( ai == i )&&( aj == j ))returntrue;if(( e2i == i )&&( e2j == j ))returntrue;if(( e3i == i )&&( e3j == j ))returntrue;// variable objectsif( GRID[i][j]== GRID_WALL )returntrue;// fixed objectsif( GRID[i][j]== GRID_MAZE )returntrue;returnfalse;}function translate ( x ){return( x -( MAXPOS/2));}function loadTextures(){var manager =new THREE.LoadingManager();var loader =new THREE.OBJLoader( manager );
loader.load("/uploads/geoghen4/sheep.obj", buildenemy1 );
loader.load("/uploads/geoghen4/sheep.obj", buildenemy2 );
loader.load("/uploads/geoghen4/sheep.obj", buildenemy3 );// load simple OBJ
loader.load("/uploads/geoghen4/dog.obj", buildagent );var loader1 =new THREE.TextureLoader();
loader1.load ('/uploads/geoghen4/fence.png',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintWalls (new THREE.MeshBasicMaterial({ map: thetexture }));});var loader2 =new THREE.TextureLoader();
loader2.load ('/uploads/geoghen4/fence.png',function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
paintMaze (new THREE.MeshBasicMaterial({ map: thetexture }));});}function buildenemy1 ( object ){
object.scale.multiplyScalar (100);// make 3d object n times bigger
object.traverse( paintEnemy );
theenemy1 = object;
threeworld.scene.add( theenemy1 );}function buildenemy2 ( object ){
object.scale.multiplyScalar (100);// make 3d object n times bigger
object.traverse( paintEnemy );
theenemy2 = object;
threeworld.scene.add( theenemy2 );}function buildenemy3 ( object ){
object.scale.multiplyScalar (100);// make 3d object n times bigger
object.traverse( paintEnemy );
theenemy3 = object;
threeworld.scene.add( theenemy3 );}function paintEnemy ( child ){if( child instanceof THREE.Mesh){
child.material.map = THREE.ImageUtils.loadTexture("/uploads/geoghen4/wool.jppg");}}function buildagent ( object ){// make 3d object n times bigger
object.traverse( paintAgent );
theagent = object;
threeworld.scene.add( theagent );}function paintAgent ( child ){if( child instanceof THREE.Mesh){
child.material.map = THREE.ImageUtils.loadTexture("/uploads/geoghen4/dogfurr.jpg");}}function initSkybox(){// urban photographic skyboxes, credit:// http://opengameart.org/content/urban-skyboxesvar materialArray =[(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/side3.png"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/side1.png"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/sky.png"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/ground.png"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/side2.png"), side: THREE.BackSide})),(new THREE.MeshBasicMaterial({ map: THREE.ImageUtils.loadTexture("/uploads/geoghen4/side4.png"), 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}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]= GRID_WALL ;}}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]== GRID_WALL ){var shape =new THREE.BoxGeometry( squaresize, squaresize,10);var thecube =new THREE.Mesh( shape );
thecube.material.color.setHex(0xffffff);if((i ==0)||(i ==19)){
thecube.rotation.y =Math.PI /2;}
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.material.side = THREE.DoubleSide;
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 initLogicalMaze(){for(var i =1; i <=6; i++){
GRID[6][i]= GRID_MAZE ;
GRID[12][i]= GRID_MAZE ;}for(var i =7; i <12; i++){
GRID[i][6]= 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]== GRID_MAZE ){var shape =new THREE.BoxGeometry( squaresize, squaresize,10);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 = 0;if((i ==6)||(i ==12)){
thecube.rotation.y =Math.PI /2;}
thecube.material.side = THREE.DoubleSide;
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;}}// --- enemy functions -----------------------------------function drawEnemys()// given e1i, e1j, draw it{if( theenemy1 ){var x = translate ( e1i * squaresize );var z = translate ( e1j * squaresize );var y =(-1* squaresize );
theenemy1.position.x = x;
theenemy1.position.y = y;
theenemy1.position.z = z;
threeworld.lookat.copy ( theenemy1.position );// if camera moving, look back at where the enemy is
threeworld.lookat.y =( squaresize *1.5);// point camera higher up}if( theenemy2 ){var x = translate ( e2i * squaresize );var z = translate ( e2j * squaresize );var y =(-1* squaresize );
theenemy2.position.x = x;
theenemy2.position.y = y;
theenemy2.position.z = z;//threeworld.lookat.copy ( theenemy2.position ); // if camera moving, look back at where the enemy is//threeworld.lookat.y = ( squaresize * 1.5 ); // point camera higher up}if( theenemy3 ){var x = translate ( e3i * squaresize );var z = translate ( e3j * squaresize );var y =(-1* squaresize );
theenemy3.position.x = x;
theenemy3.position.y = y;
theenemy3.position.z = z;//threeworld.lookat.copy ( theenemy2.position ); // if camera moving, look back at where the enemy is//threeworld.lookat.y = ( squaresize * 1.5 ); // point camera higher up}}function initLogicalEnemy(){// start in random location:var i, j;do{
i = randomintAtoB(1,gridsize-2);
j = randomintAtoB(1,gridsize-2);}while( occupied(i,j)|| zone(i,j)==2);// search for empty square
e1i = i;
e1j = j;do{
i = randomintAtoB(1,gridsize-2);
j = randomintAtoB(1,gridsize-2);}while( occupied(i,j)|| zone(i,j)==2);// search for empty square
e2i = i;
e2j = j;do{
i = randomintAtoB(1,gridsize-2);
j = randomintAtoB(1,gridsize-2);}while( occupied(i,j)|| zone(i,j)==2);// search for empty square
e3i = i;
e3j = j;}function enemyGetAction(i,j){if(zone(i,j)==5&& zone(ai,aj)==5&& gateLifted ==false){
liftGate();}elseif(zone(i,j)!=5&& zone(ai,aj)!=5&& gateLifted ==true){
lowerGate();}if(zone(i,j)==2){if(randomBoolean()){return( ACTION_UP );}else{return randomPick ( ACTION_LEFT, ACTION_RIGHT );}}if((aj === j)&&((ai-1)=== i )){return( ACTION_LEFT );}elseif((aj === j)&&((ai+1)=== i )){return( ACTION_RIGHT );}elseif((ai === i)&&((aj-1)=== j )){return(ACTION_UP);}elseif((ai === i)&&((aj+1)=== j )){return(ACTION_DOWN);}else{if(randomBoolean()){return randomPick(ACTION_DOWN, ACTION_UP)}else{return randomPick(ACTION_LEFT, ACTION_RIGHT)}}}function moveLogicalEnemy(id){switch(id){case1:var a = enemyGetAction(e1i,e1j);var i = e1i;var j = e1j;break;case2:var a = enemyGetAction(e2i,e2j);var i = e2i;var j = e2j;break;default:var a = enemyGetAction(e3i,e3j);var i = e3i;var j = e3j;}if( a == ACTION_LEFT ){ i--;}elseif( a == ACTION_RIGHT ){ i++;}elseif( a == ACTION_UP ){ j--;}elseif( a == ACTION_DOWN ){ j++;}if(! occupied(i,j)){if(true){if( a == ACTION_LEFT ){ rotateEnemyTowards ( id,3*(Math.PI /2));}elseif( a == ACTION_RIGHT ){ rotateEnemyTowards ( id,1*(Math.PI /2));}elseif( a == ACTION_UP ){ rotateEnemyTowards ( id,2*(Math.PI /2));}elseif( a == ACTION_DOWN ){ rotateEnemyTowards ( id,0*(Math.PI /2));}}switch(id){case1:
e1i = i;
e1j = j;break;case2:
e2i = i;
e2j = j;break;default:
e3i = i;
e3j = j;}}}const INTERIMROT =10;// number of interim rotations drawn when model turns roundfunction rotateEnemyTowards (id, newRotation ){switch(id){case1:if( enemy1Rotation == newRotation )return;// elsevar x =( enemy1Rotation + newRotation )/2;
theenemy1.rotation.set(0, x,0);
enemy1Rotation = x;break;case2:if( enemy2Rotation == newRotation )return;// elsevar x =( enemy2Rotation + newRotation )/2;
theenemy2.rotation.set(0, x,0);
enemy2Rotation = x;break;case3:if( enemy3Rotation == newRotation )return;// elsevar x =( enemy3Rotation + newRotation )/2;
theenemy3.rotation.set(0, x,0);
enemy3Rotation = x;}}// --- agent functions -----------------------------------function drawAgent()// given ai, aj, draw it{if( theagent ){var x = translate ( ai * squaresize );var z = translate ( aj * squaresize );var y =(-1* squaresize );
theagent.position.x = x;
theagent.position.y = y;
theagent.position.z = z;
threeworld.follow.copy ( theagent.position );// follow vector = agent position (for camera following agent)
threeworld.follow.y =( squaresize *1.5);// put camera higher up}}function initLogicalAgent(){// start in random location:
ai =9;
aj =4;}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)){if(true){// if going to actually move, then turn body towards move// rotate by some amount of radians from the normal position// in degrees: +0, +90, +180, +270if( a == ACTION_LEFT ){ rotateAgentTowards (3*(Math.PI /2));}elseif( a == ACTION_RIGHT ){ rotateAgentTowards (1*(Math.PI /2));}elseif( a == ACTION_UP ){ rotateAgentTowards (2*(Math.PI /2));}elseif( a == ACTION_DOWN ){ rotateAgentTowards (0*(Math.PI /2));}}
ai = i;
aj = j;if(zone(ai,aj)==2&& gateLifted ==false){
liftGate();}}}function rotateAgentTowards ( newRotation ){if( agentRotation == newRotation )return;// elsevar x =( agentRotation + newRotation )/2;
theagent.rotation.set(0, x,0);
agentRotation = x;}// --- score: -----------------------------------function badstep()// is the enemy within one square of the agent{if((Math.abs(e1i - ai)<2)&&(Math.abs(e1j - aj)<2))returntrue;elsereturnfalse;}function updateStatus(){var status = sheepCaught()+" sheep caught out of "+3;
$("#user_span1").html( status );}//--- public functions / interface / API ----------------------------------------------------------// must have this public variable:this.endCondition;// If set to true, run will end.this.newRun =function(){this.endCondition =false;
badsteps =0;
goodsteps =0;
step =0;// define logical data structure for the World, even if no graphical representation:
initGrid();
initLogicalWalls();
initLogicalMaze();
initLogicalAgent();
initLogicalEnemy();//initLogicalEnemy2();if(true){
threeworld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );// lightvar ambient =new THREE.AmbientLight();
threeworld.scene.add( ambient );var thelight =new THREE.DirectionalLight( LIGHTCOLOR,3);
thelight.position.set( startRadiusConst, startRadiusConst, startRadiusConst );
threeworld.scene.add(thelight);// musicvar x ="<audio id=theaudio src=http://student.computing.dcu.ie/~geoghen4/bennyhill.mp3 autoplay loop> </audio>";
$("#user_span2").html( x );// music credit// http://www.dl-sounds.com/royalty-free/suspense-strings/
initSkybox();
initThreeWalls();
initThreeMaze();
loadTextures();// will return sometime later, but can go ahead and render now}};this.getState =function(){var x =[ ai, aj, e1i, e1j, e2i, e2j, e3i, e3j ];return( x );};this.takeAction =function( a ){
step++;if(sheepCaught()==3&& zone(ai,aj)==8){
lowerGate();this.endCondition =true;}
moveLogicalAgent(a);if(( step %2)==0){
moveLogicalEnemy(1);
moveLogicalEnemy(2);
moveLogicalEnemy(3);}// slow the enemy down to every nth stepif( badstep())
badsteps++;else
goodsteps++;if(true){
drawAgent();
drawEnemys();//drawEnemy2();
updateStatus();}};this.endRun =function(){
console.log("Run Ended");};this.getScore =function(){return goodsteps;};}//---- end of World class -------------------------------------------------------