// Cloned by rohan on 4 Nov 2022 from World "AI_Assignment2022_world1_Scooby-Do" by rohan // Please leave this clone trail here.// Cloned by rohan on 1 Nov 2022 from World "Complex World" by Starter user // Please leave this clone trail here.// ==== 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.// Rohan: -------to set up no. of walls to be moved and timestep for the moving walls------- const WALLMOVENO=2;// if set zero no walls will moveconst WALLMOVETICK=2;// if set as zero then no walls will move in milliseconds
let wallList=[]//---- global constants: -------------------------------------------------------const show3d =true;// Switch between 3d and 2d view (both using Three.js) const TEXTURE_WALL ='/uploads/rohanb456/depositphotos_3625609-stock-photo-white-brick-wall.jpg';const TEXTURE_MAZE ='/uploads/rohanb456/kurinsky_lead_house-1280x600scooby-do-walls.jpg';const TEXTURE_AGENT ='/uploads/rohanb456/scrappy.png';const TEXTURE_ENEMY ='/uploads/rohanb456/Scooby-Doo.png';// Credits// https://en.wikipedia.org/wiki/Scooby-Doo_(character)// https://www.pinterest.ie/pin/771804454880520113/// https://in.pinterest.com/pin/682436149780330845/// credits:// http://commons.wikimedia.org/wiki/File:Old_door_handles.jpg// https://www.cartoonbrew.com/how-to/how-to-create-a-scooby-doo-haunted-house-step-by-step-with-scoob-production-designer-michael-kurinsky-192777.html// 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).jpgconst MUSIC_BACK ='/uploads/rohanb456/8d82b5_Scooby_Doo_Theme_Song.mp3';const SOUND_ALARM ='/uploads/rohanb456/hello.mp3';// credits:// http://soundfxcenter.com/download-sound/scooby-doo-theme-song/// https://www.soundboard.com/sb/sound/20247const gridsize =30;// number of squares along side of world //const NOBOXES = Math.trunc ( (gridsize * gridsize) / 10 );// density of maze - number of internal boxes// (bug) use trunc or can get a non-integer const NOBOXES =(gridsize * gridsize)/7;const squaresize =100;// size of square in pixelsconst 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 atconst 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/starter/dawnmountain-xpos.png",
"/uploads/starter/dawnmountain-xneg.png",
"/uploads/starter/dawnmountain-ypos.png",
"/uploads/starter/dawnmountain-yneg.png",
"/uploads/starter/dawnmountain-zpos.png",
"/uploads/starter/dawnmountain-zneg.png"
];*///const SKYBOX_ARRAY = ["/uploads/starter/dawnmountain-xpos.png"];// space skybox, credit:// http://en.spaceengine.org/forum/21-514-1// x,y,z labelled differently/*
const SKYBOX_ARRAY = [
"/uploads/starter/sky_pos_z.jpg",
"/uploads/starter/sky_neg_z.jpg",
"/uploads/starter/sky_pos_y.jpg",
"/uploads/starter/sky_neg_y.jpg",
"/uploads/starter/sky_pos_x.jpg",
"/uploads/starter/sky_neg_x.jpg"
];
*/// urban photographic skyboxes, credit:// http://opengameart.org/content/urban-skyboxesconst SKYBOX_ARRAY =["/uploads/starter/posx.jpg","/uploads/starter/negx.jpg","/uploads/starter/posy.jpg","/uploads/starter/negy.jpg","/uploads/starter/posz.jpg","/uploads/starter/negz.jpg"];// ===================================================================================================================// === 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 squareconst GRID_BLANK =0;const GRID_WALL =1;const GRID_MAZE =2;var BOXHEIGHT;// 3d or 2d box height var GRID =newArray(gridsize);// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array var grid =newArray(gridsize);var theagent, theenemy;var count =0;var wall_texture, agent_texture, enemy_texture, maze_texture;// enemy and agent position on squaresvar ei, ej, ai, aj;var badsteps;var goodsteps;const diag =false;// sets diagonal movesfunction loadResources()// asynchronous file loads - call initScene() when all finished {var loader1 =new THREE.TextureLoader();var loader2 =new THREE.TextureLoader();var loader3 =new THREE.TextureLoader();var loader4 =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();});
loader4.load ( TEXTURE_MAZE,function( thetexture ){
thetexture.minFilter = THREE.LinearFilter;
maze_texture = thetexture;if( asynchFinished()) initScene();});}function asynchFinished()// all file loads returned {if( wall_texture && agent_texture && enemy_texture && maze_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]== GRID_WALL )returntrue;// fixed objects if( GRID[i][j]== GRID_MAZE )returntrue;returnfalse;}// 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;}var path =[];// Rohan: basically itemPoint which has attributes such as x,y coordinates// to check if the cell is wall or maze we use the isitWall param// at the time of initialization we are assigning the f and g values to infinityfunction itemPoint(i, j, isitWall){this.i = i;this.j = j;this.f =Number.MAX_VALUE;this.g =Number.MAX_VALUE;this.h =0;this.neighbors =[];this.wall =false;this.previous =null;if(isitWall){this.wall =true;}// neighbours are added if only movement is possible// maze and walls are removedthis.addNeighbors =function(grid){var i =this.i;var j =this.j;if(i < gridsize -1&&!grid[i +1][j].wall)this.neighbors.push(grid[i +1][j]);if(i >0&&!grid[i -1][j].wall)this.neighbors.push(grid[i -1][j]);if(j < gridsize -1&&!grid[i][j +1].wall)this.neighbors.push(grid[i][j +1]);if(j >0&&!grid[i][j -1].wall)this.neighbors.push(grid[i][j -1]);// Set the diagonal Moves such that if they are allowed we need to add the diag neighborif(diag){if(i >0&& j >0&&!grid[i -1][j -1].wall)this.neighbors.push(grid[i -1][j -1]);if(i < gridsize -1&& j >0&&!grid[i +1][j -1].wall)this.neighbors.push(grid[i +1][j -1]);if(i >0&& j < gridsize -1&&!grid[i -1][j +1].wall)this.neighbors.push(grid[i -1][j +1]);if(i < gridsize -1&& j < gridsize -1&&!grid[i +1][j +1].wall)this.neighbors.push(grid[i +1][j +1]);}}}var openList =[];var closedList =[];function initScene()// all file loads have returned {var i,j, shape, thecube;// set up GRID as 2D arrayfor( i =0; i < gridsize ; i++){
GRID[i]=newArray(gridsize);
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]= GRID_WALL;
grid[i][j]=new itemPoint(i, j,true);
shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, 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]= GRID_BLANK;
grid[i][j]=new itemPoint(i, j,false);}// set up maze for(var c=1; c <= NOBOXES ; c++){
i = AB.randomIntAtoB(1,gridsize-2);// inner squares are 1 to gridsize-2
j = AB.randomIntAtoB(1,gridsize-2);
GRID[i][j]= GRID_MAZE;
grid[i][j].wall =true;
shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
thecube =new THREE.Mesh( shape );
thecube.material =new THREE.MeshBasicMaterial({ map: maze_texture });
thecube.position.copy ( translate(i,j));// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates
wallList.push(thecube);// push thecube values on to the arrayABWorld.scene.add(thecube);}// All the neighborsfor(var i =0; i < gridsize; i++)for(var j =0; j < gridsize; j++)
grid[i][j].addNeighbors(grid);// set up enemy // start in random locationdo{
i = AB.randomIntAtoB(1,gridsize-2);
j = AB.randomIntAtoB(1,gridsize-2);}while( occupied(i,j));// search for empty square
ei = i;
ej = j;
shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theenemy =new THREE.Mesh( shape );
theenemy.material =new THREE.MeshBasicMaterial({ map: enemy_texture });ABWorld.scene.add(theenemy);
drawEnemy();// set up agent // start in random locationdo{
i = AB.randomIntAtoB(1,gridsize-2);
j = AB.randomIntAtoB(1,gridsize-2);}while( occupied(i,j));// search for empty square
ai = i;
aj = j;
shape =new THREE.BoxGeometry( squaresize, BOXHEIGHT, squaresize );
theagent =new THREE.Mesh( shape );
theagent.material =new THREE.MeshBasicMaterial({ map: agent_texture });ABWorld.scene.add(theagent);
drawAgent();// finally skybox // setting up skybox is simple // just pass it array of 6 URLs and it does the asych load ABWorld.scene.background =new THREE.CubeTextureLoader().load ( SKYBOX_ARRAY,function(){ABWorld.render();
AB.removeLoading();
AB.runReady =true;// start the run loop});}// --- 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)}// Rohan: func to delete element from the arrayfunction arrayRemoval(arr, elt){// could use indexOf here instead to be more efficientfor(var i = arr.length -1; i >=0; i--)if(arr[i]== elt)
arr.splice(i,1);}// ------ take actions -----------------------------------// Rohan: Manhattan distance implementation heuristic functionfunction heu(a,b){returnMath.abs(a.i-b.i)+Math.abs(a.j-b.j);//return Math.pow(Math.abs(a.i - b.i),2) + Math.pow(Math.abs(a.j - b.j),2); // uncomment to perform euclidean dist}function moveLogicalEnemy(){// clearing the openList, closedList and path before each A* runwhile(openList.length >0){
openList.pop();}while(closedList.length >0){
closedList.pop();}while(path.length >0){
path.pop();}
clearPreviousAttri();var n = astar (grid[ei][ej], grid[ai][aj]);
printPath(n);}// Rohan: as the code was entering infinite loop while tracking the path the below func was built// below function clears the attribute before the start of A* funcfunction clearPreviousAttri(){for(var i=0; i < gridsize-1; i++){for(var j=0; j < gridsize-1; j++){
grid[i][j].previous =null;}}}// Rohan: A* search func takes two inputs firstly enemy position which is start point and agent position which is the target.// target and start node is sent in the below functionfunction astar(start, target){
start.f = start.g + heu(start, target);
openList.push(start);while(openList.length >0){// checks for the node in openList with min f value. // can optimized by implementing Min Heap which will reduce the Time Complexityvar temp =0;for(var i =0; i < openList.length; i++){if(openList[i].f < openList[temp].f){
temp = i;}}var current = openList[temp];if(current == target){return current;}// to get the path check with every neighborfor(var i =0; i < current.neighbors.length; i++){var neighbor = current.neighbors[i];var G = current.g + heu(current, neighbor);// check for the presence of neighbour in openList and ClosedList// as we have removed walls and maze from neighbor list we can skip neighbor.wallif(!openList.includes(neighbor)&&!closedList.includes(neighbor)&&!neighbor.wall){
neighbor.previous = current;
neighbor.g = current.g + heu(current, neighbor);
neighbor.f = neighbor.g + heu(neighbor, target);
openList.push(neighbor)}else{if(G < neighbor.g){
neighbor.previous = current;
neighbor.g = G;
neighbor.f = neighbor.g + heu(neighbor, target);if(closedList.includes(neighbor)){
arrayRemoval(closedList,neighbor);
openList.push(neighbor);}}}}
arrayRemoval(openList,current);
closedList.push(current);}returnnull;}// Rohan: to tracks the path from start node to target node below func can be used// to go step by step and not to move at once function printPath(target){var n = target;if(n ===null)return;// the previous nodes are pushed into the path list as they are tracked backwhile(n.previous !==null){
path.push(n);
n = n.previous;}
path.push(n);// we check path length > 2 to trap the agentif(path.length >2){// length - 1 will be the starting point which is the last element path so path length - 2 which is the next move is picked if(!occupied(path[path.length -2].i, path[path.length -2].j)){
drawPath(ei, ej, path[path.length -2].i, path[path.length -2].j);// the current and the next position is passed as the corrdinates
ei = path[path.length -2].i;
ej = path[path.length -2].j;}}// path[0] and path[1] is the position of agent and enemy, where path length == 2 means we have 2 elements in the path list// that is similar to src and dest postion in A* // which also shows that one cell is away from enemy and can trap the agentelse{
trapAgent();}}var points =[];// Rohan: to draw the pathfunction drawPath(x1, y1, x2, y2){
points.push(translate(x1, y1));
points.push(translate(x2, y2));// create Tube Geometryvar tubeGeometry =new THREE.TubeGeometry(new THREE.CatmullRomCurve3(points),512,// path segments0.5,// THICKNESS8,//Roundness of Tubefalse//closed);//geometry = new THREE.BufferGeometry().setFromPoints( points );var material =new THREE.LineBasicMaterial({ color :0x00ff00, wireframe:true, linewidth:10, linecap:'round', linejoin:'round'});
let line =new THREE.Line( tubeGeometry, material );//line = new THREE.Line( tubeGeometry, material );ABWorld.scene.add(line);}// Rohan: below func traps agent when A* has two or less elements in path// which defines that the enemy is close to agent which means that there is difference in one cellvar count =0;function trapAgent(){// firstly check if the the agent is surrounded by all the sides so that the enemy can trap the agent// else we can move the enemy horizontally or vertically by one position based on count variableif(!threesidesTrapAgent()){var neighbors = grid[ei][ej].neighbors;for(var i =0; i < neighbors.length; i++){if(Math.abs(neighbors[i].j - aj)==1||Math.abs(neighbors[i].i - ai)==1){if(count %2===0&&!occupied(ai, aj +1)){
ei = ai;
ej = aj +1;}elseif(count %2!==0&&!occupied(ai +1, aj)){
ei = ai +1;
ej = aj;}
count++;}}}}// Rohan: here we check if the agent is surrounded by three sides , if so then we trap itfunction threesidesTrapAgent(){if(occupied (ai-1,aj)&& occupied (ai+1,aj)&& occupied (ai,aj+1)){if(!occupied(ai,aj-1)){
ei = ai;
ej = aj -1;}returntrue;}elseif(occupied (ai-1,aj)&& occupied (ai+1,aj)&& occupied (ai,aj-1)){if(!occupied (ai,aj+1)){
ei = ai;
ej = aj +1;}returntrue;}elseif(occupied (ai-1,aj)&& occupied (ai,aj+1)&& occupied (ai,aj-1)){if(!occupied (ai+1,aj)){
ei = ai +1;
ej = aj;}returntrue;}elseif(occupied (ai+1,aj)&& occupied (ai,aj+1)&& occupied (ai,aj-1)){if(!occupied (ai-1,aj)){
ei = ai -1;
ej = aj;}returntrue;}returnfalse;}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;}}// --- key handling --------------------------------------------------------------------------------------// This is hard to see while the Mind is also moving the agent:// AB.mind.getAction() and AB.world.takeAction() are constantly running in a loop at the same time // have to turn off Mind actions to really see user key control // we will handle these keys: var OURKEYS =[37,38,39,40];function ourKeys ( event ){return( OURKEYS.includes ( event.keyCode ));}function keyHandler ( event ){if(! AB.runReady )returntrue;// not ready yet // if not one of our special keys, send it to default key handling:if(! ourKeys ( event ))returntrue;// else handle key and prevent default handling:if( event.keyCode ==37) moveLogicalAgent ( ACTION_LEFT );if( event.keyCode ==38) moveLogicalAgent ( ACTION_DOWN );if( event.keyCode ==39) moveLogicalAgent ( ACTION_RIGHT );if( event.keyCode ==40) moveLogicalAgent ( ACTION_UP );// when the World is embedded in an iframe in a page, we want arrow key events handled by World and not passed up to parent
event.stopPropagation(); event.preventDefault();returnfalse;}// --- 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 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(a)// 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 x = AB.world.getState();
AB.msg (" Step: "+ AB.step +" x = ("+ x.toString()+") a = ("+ a +") ");}function updateStatusAfter()// agent and enemy have moved, can calculate score{// new state after both have movedvar y = AB.world.getState();var score =( goodsteps / AB.step )*100;
AB.msg (" y = ("+ y.toString()+") <br>"+" Bad steps: "+ badsteps +" Good steps: "+ goodsteps +" Score: "+ score.toFixed(2)+"% ",2);}
AB.world.newRun =function(){
AB.loadingScreen();
AB.runReady =false;
badsteps =0;
goodsteps =0;if( show3d ){
BOXHEIGHT = squaresize;ABWorld.init3d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}else{
BOXHEIGHT =1;ABWorld.init2d ( startRadiusConst, maxRadiusConst, SKYCOLOR );}
loadResources();// aynch file loads // calls initScene() when it returns
document.onkeydown = keyHandler;};
AB.world.getState =function(){var x =[ ai, aj, ei, ej ];return( x );};
AB.world.takeAction =function( a ){
updateStatusBefore(a);// show status line before moves
moveLogicalAgent(a);//if ( ( AB.step % 2 ) == 0 ) // slow the enemy down to every nth step
moveLogicalEnemy();// Rohan: calling the moving walls function per timestep//if ( ( AB.step % 2 ) == 0 )
wallsmovePerStep();//wallstobeMoved()if( badstep()) badsteps++;else goodsteps++;
drawAgent();
drawEnemy();
updateStatusAfter();// show status line after moves if( agentBlocked())// if agent blocked in, run over {
AB.abortRun =true;
goodsteps =0;// you score zero as far as database is concerned
musicPause();
soundAlarm();}};
AB.world.endRun =function(){
musicPause();if( AB.abortRun ) AB.msg (" <br> <font color=red> <B> Agent trapped. Final score zero. </B> </font> ",3);else AB.msg (" <br> <font color=green> <B> Run over. </B> </font> ",3);};
AB.world.getScore =function(){// only called at end - do not use AB.step because it may have just incremented past AB.maxStepsvar s =( goodsteps / AB.maxSteps )*100;// float like 93.4372778 var x =Math.round (s *100);// 9344return( x /100);// 93.44};// --- music and sound effects ----------------------------------------var backmusic = AB.backgroundMusic ( MUSIC_BACK );function musicPlay(){ backmusic.play();}function musicPause(){ backmusic.pause();}function soundAlarm(){var alarm =newAudio( SOUND_ALARM );
alarm.play();// play once, no loop }// Rohan: moving wall function, has to have WALLMOVETICK > 0 else no walls will move// first check the condition// then execute the while condition that moves the wallsfunction wallstobeMoved(){
let movedWalls=[];const time=newDate();if(WALLMOVENO<NOBOXES&&WALLMOVETICK>0){while(movedWalls.length<WALLMOVENO&&!agentBlocked()){
movingWalls=wallList.sample();
level=notConverted(movingWalls.position);
x=[level.x-1,level.x,level.x+1].sample();
y=[level.y-1,level.y,level.y+1].sample();
newLevel=translate(x,y);
occupied(x,y)||movedWalls.includes([newLevel.x,newLevel.y].toString())||(movingWalls.position.copy(newLevel),movedWalls.push([newLevel.x,newLevel.y].toString()),
GRID[level.x][level.y]=GRID_BLANK,
GRID[x][y]=GRID_MAZE)
console.log("walls moving from position: "+level.x+","+level.y+"to: "+x+","+y);
console.log("walls that have moved: "+movedWalls.length);
console.log("given time is : "+60*time.getMinutes()+time.getSeconds());}}}// Rohan: to move walls per timestep in seconds
let checkout=0;function wallsmovePerStep(){
setTimeout(function(){
wallstobeMoved(),
checkout++;},checkout*WALLMOVETICK*1000);}function notConverted(r){var v =new THREE.Vector3();
v.x =( r.x+MAXPOS/2)/squaresize ;
v.y =( r.z+MAXPOS/2)/squaresize ;return v;}Array.prototype.sample=function(){returnthis[Math.floor(Math.random()*this.length)]};