Code viewer for World: A* - Catch me if you can
// Cloned by Harpreet Singh on 1 Nov 2019 from World "Complex 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.
// ==================================================================================================================

// =============================================================================================
// 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.
// =============================================================================================

// =============================================================================================
// Media Credits:
//https://www.appannie.com/en/apps/ios/app/1344484784/
//https://img.pixers.pics
//https://www.thoughtco.com/thmb/
//https://sophosnews.files.wordpress.com
//
//SKYBOX Graphics: https://threejsfundamentals.org/threejs/lessons/threejs-backgrounds.html
//Audio Files: https://freesound.org/
// =============================================================================================

//************************************** CODE SECTIONS ***************************************************************
/*
    All code has been reorganised to make it easily readable.
    Didn't partition code to different JS files but organised similar functions in various sections
    
    Code section from top to bottom are as follows:
    
    01. Tweaker Box and global variables declaration/initialisation
    02. AB World Key Functions:                                     newrun, getstate, takeaction, endrun, getscore
    03. Setup and Utility Functions:                                loadResources, initScene
    04. Functions to draw moving objects:                           drawAgent, drawEnemy
    05. Functions for status checks and agent actions               asynchFinished, occupied, updateStatusBefore, updateStatusAfter, badstep, agentBlocked
    06. Agent and Enemy moves                                       moveLogicalAgent, moveLogicalEnemy
    07. A* search                                                   findShortestPath
    08. Grid Cells - create object & assign properties for each cell
    09. Conversion and Utility functions                            translate, removeFromArray, heuristic
    10. Music and sound effects                                     musicPlay, musicPause, soundAlarm
    11. Event handlers                                              ourKeys, keyHandler
*/
//********************************************************************************************************************


// ===================================================================================================================
// === Section 01. Tweaker Box and global variables declaration/initialisation======================================== 
// ===================================================================================================================

//---- global constants: -------------------------------------------------------
const show3d = true;           //Switch between 3d and 2d view (both using Three.js) 
const slowmotion = false;       //the run is slowed down to 1 second per cycle. default is 100 ms/cycle
const diagonalmoves = false;    //allow the enemy chase with diagonal moves.
const tracesearch = false;      //for every search trace full path back. memory intensive and likely to throw memory exception

const TEXTURE_WALL 	= '/uploads/hsingh/brick3.jpg' ;
const TEXTURE_MAZE 	= '/uploads/hsingh/patternmaze.jpg' ;
const TEXTURE_AGENT = '/uploads/hsingh/smiley2.jpg' ;
const TEXTURE_ENEMY = '/uploads/hsingh/1572624791.png' ;

const MUSIC_BACK  = '/uploads/hsingh/ailoop.wav' ;
const SOUND_ALARM = '/uploads/hsingh/jolly-fanfares.wav' ;

const gridsize = 20;                                            // number of squares along side of world	   
const NOBOXES =  Math.trunc((gridsize * gridsize) / 10 );       // density of maze - number of internal boxes

const squaresize = 100;					                        // size of square in pixels

const 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 at
const maxRadiusConst 		= MAXPOS * 10  ;		            // maximum distance from camera we will render things 

//--- change ABWorld defaults: -------------------------------
if (slowmotion) AB.clockTick = 1000; else 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.

AB.headerRHS();
AB.headerWidth (300);

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 

const SKYBOX_ARRAY = [										 
            "/uploads/hsingh/pos-x.jpg",
            "/uploads/hsingh/neg-x.jpg",
            "/uploads/hsingh/pos-y.jpg",
            "/uploads/hsingh/neg-y.jpg",
            "/uploads/hsingh/pos-z.jpg",
            "/uploads/hsingh/neg-z.jpg"
            ];
            
// ===================================================================================================================
// === End of 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 BOXHEIGHT;		                        // 3d or 2d box height 

var the_agent, the_enemy;
var wall_texture, agent_texture, enemy_texture, maze_texture; 

// enemy and agent position on squares
var ei, ej, ai, aj;

var badsteps;
var goodsteps;

var grid 	= new Array(gridsize);			// can query GRID about whether squares are occupied, will in fact be initialised as a 2D array   

//Enemy search move check variables (A*)--------------------------
var openSet = [];           
var closedSet = [];
var path = [];
var start, end;

// ===================================================================================================================
// === Section 02. AB World Key Functions=============================================================================
// === newrun, getstate, takeaction, endrun, getscore ================================================================
// ===================================================================================================================
AB.world.newRun = function(){
	AB.loadingScreen();     //Ancient brain splash screen that shows up till all graphics and media is loaded and ready to run
	
	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 -- we can remove this to make enemy more aggressive
        moveLogicalEnemy();
    
    if (badstep()) badsteps++; else goodsteps++;
    
    drawAgent();
    drawEnemy();
    updateStatusAfter();			// show status line after moves 
    
    // if agent blocked in, run over 
    if (agentBlocked()){
        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.maxSteps
    
    var s = (goodsteps/AB.maxSteps) * 100;   // float like 93.4372778 
    var x = Math.round (s * 100);                // 9344
    return (x/100);                          // 93.44
};

// ===================================================================================================================
// === Section 03. Setup and Utility Functions========================================================================
// === loadResources, initScene ======================================================================================
// ===================================================================================================================

//Functions to set the scene, create agent and enemy and then kick off the play
// asynchronous file loads - call initScene() when all finished 
function loadResources(){
	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 initScene(){
    var i, j, shape, the_cube;
    
    for(i=0; i<gridsize ; i++){
        grid[i] = new Array(gridsize ); //Create a 2D grid with rows and columns -- we are keeping same rows and columns
    }
    
    //Initialize each grid cell object with default properties (f,g,h etc.)
    for(i=0; i<gridsize; i++){
      for(j=0; j<gridsize; j++){
          grid[i][j] = new GridCell(i, j);
          
          //while creating the grid cell also initialize the side walls
          if ((i==0) || (i==gridsize-1) || (j==0) || (j==gridsize-1)){
              //MODIFICATION TO ORIGINAL CODE- tracking all states through one object
              grid[i][j].external_wall = true;          //USED the boolean flags within GridCell to track wall, maze, open or occupied space
              
              shape    = new THREE.BoxGeometry (squaresize, BOXHEIGHT, squaresize);
              the_cube  = new THREE.Mesh(shape);
              the_cube.material = new THREE.MeshBasicMaterial({ map: wall_texture });
              
              the_cube.position.copy ( translate(i,j) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
              ABWorld.scene.add(the_cube);
          }
          
          //also set random maze, changed this from original code.
          if (Math.random(1)<0.20 && !grid[i][j].external_wall){
              grid[i][j].internal_maze_block = true;    //USED the boolean flags within GridCell to track wall, maze, open or occupied space
              
              
              shape    = new THREE.BoxGeometry ( squaresize, BOXHEIGHT, squaresize );			 
		      the_cube  = new THREE.Mesh( shape );
		      the_cube.material = new THREE.MeshBasicMaterial( { map: maze_texture } );		  

		      the_cube.position.copy ( translate(i,j) ); 		  	// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
		      ABWorld.scene.add(the_cube);
          }
      }
    }
	
	//Add neighbours for each grid cell as we need to know neighbors for A* search while visiting a node during search
	for(var i=0; i<gridsize; i++){
      for(var j=0; j<gridsize; j++){
          grid[i][j].addNeighbours(grid);
      }
    }
    
    // set up enemy 
	// start in random location
	
	do {
	    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 );			 
	the_enemy = new THREE.Mesh(shape);
 	the_enemy.material =  new THREE.MeshBasicMaterial( { map: enemy_texture } );
	ABWorld.scene.add(the_enemy);
	drawEnemy();
	
	
	// set up agent 
	// start in random location
	do{
	    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 );			 
	the_agent = new THREE.Mesh( shape );
	the_agent.material =  new THREE.MeshBasicMaterial( { map: agent_texture } );
	ABWorld.scene.add(the_agent);
	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
  	});
}

// ===================================================================================================================
// === Section 04. Functions to draw moving objects===================================================================
// === drawAgent, drawEnemy ==========================================================================================
// ===================================================================================================================

function drawAgent(){
    // given ai, aj, draw it 
    the_agent.position.copy(translate(ai,aj)); 		// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
    ABWorld.follow.copy(the_agent.position);			// follow vector = agent position (for camera following agent)
}

function drawEnemy(){
    // given ei, ej, draw it 
    the_enemy.position.copy( translate(ei,ej) ); 		// translate my (i,j) grid coordinates to three.js (x,y,z) coordinates 
	ABWorld.lookat.copy( the_enemy.position );	 		// if camera moving, look back at where the enemy is  
}


// ===================================================================================================================
// === Section 05. Functions for status checks and agent actions =====================================================
// === asynchFinished, occupied, updateStatusBefore, updateStatusAfter, badstep, agentBlocked ========================
// ===================================================================================================================


//Are all files loaded to kick off the chase?
function asynchFinished(){
    return (wall_texture && agent_texture && enemy_texture && maze_texture);
}

//Check to see if this square is occupied?
// my numbering is 0 to gridsize-1
function occupied ( i, j ){
    if ((ei == i) && (ej == j)) return true;		// variable objects 
    if ((ai == i) && (aj == j)) return true;
    
    //MODIFIED BELOW CODE to check for the wall or maze using the grid cell property
    if (grid[i][j].external_wall) return true;		// fixed objects	 
    if (grid[i][j].internal_maze_block) return true;		 
    
    return false;
}

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 + " &nbsp; x = (" + x.toString() + ") &nbsp; a = (" + a + ")");
}

function updateStatusAfter(){
    // agent and enemy have moved, can calculate score
    // new state after both have moved
    
    var y = AB.world.getState();
    var score = ( goodsteps / AB.step ) * 100;
    
    AB.msg ( " &nbsp; y = (" + y.toString() + ") <br>" +
		" Bad steps: " + badsteps + 
		" &nbsp; Good steps: " + goodsteps + 
		" &nbsp; Score: " + score.toFixed(2) + "% ", 2 );
}

// is the enemy within one square of the agent
function badstep(){
    return ((Math.abs(ei - ai) < 2 ) && ( Math.abs(ej - aj) < 2));
}

// agent is blocked on all sides, run over
function agentBlocked(){
    return (
        occupied (ai-1, aj) && 
		occupied (ai+1, aj)	&&
		occupied (ai, aj+1)	&&
		occupied (ai, aj-1)
		);		
}

// ===================================================================================================================
// === Section 06. Agent and Enemy moves =============================================================================
// === moveLogicalAgent, moveLogicalEnemy ============================================================================
// ===================================================================================================================

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;   
    }
}

function moveLogicalEnemy(){
    //Call A* Search function to trace the shortest path to the agent from enemy
    
    //During first search the path from enemy to agent will take longer to search
    //Subsequent moves search will be faster as the enemy is now closely chasing the agent
    
    var winner = findShortestPath();//Search function will return the winning node or null is the search didn't find any path from enemy to agent
    var i, j;
    
    if (winner!== undefined){       //Check to ensure we have found a path to the agent
        //If search found the path to the agent we want to move enemy close to agent
        //We are using the search output to look at the node
        //As the winner node is where the agent is and we can not move enemy to agent position which is already occupied
        //So we move the enemy to one previous node in the shortest search path
        i = winner.previous.i;      //go to previous neighbour of the winning node and get i,j values
        j = winner.previous.j;
        
        
        if (tracesearch){           //If we want to trace the full path back to start during each search-- this function is memory intensive
            path = [];
            var temp = winner;
            path.push(temp);
            while (temp.previous) 
            {
                //console.log ("loop");
                path.push(temp.previous);
                temp = temp.previous;
              }
        }
    }
    
    //This check ensures we are not moving to a blocked position
    if (!occupied(i,j)){
        ei = i;
        ej = j;
    }    
}

// ===================================================================================================================
// === Section 07. A* Search =========================================================================================
// === findShortestPath ==============================================================================================
// ===================================================================================================================

function findShortestPath(){
    //Initial arrays to hold open and closed
    openSet = [];
    closedSet = [];
    
    //With every move agent and enemy position has changed to we need to capture the current location for both
    start = grid[ei][ej];       //enemy position
    end = grid[ai][aj];         //agent position
    openSet.push(start);        //this is our starting position to search from. This is the only node we know in beginning.
    
    //iterate through open
    while (openSet.length > 0){

    // Best next option
    var winner = 0;
    
    for (var i = 0; i < openSet.length; i++) 
      if (openSet[i].f < openSet[winner].f) 
        winner = i;
        
    var current = openSet[winner];

    //if current node is same as agent we have found the target.
    if (current === end){
      return current;
    }

    // Best option moves from openSet to closedSet
    removeFromArray(openSet, current);
    closedSet.push(current);

    // Check all the neighbors
    var neighbours = current.neighbours;
    
    //--- start of for loop -----------
    for (var i = 0; i < neighbours.length; i++) 
    {
      var neighbor = neighbours[i];

      // Valid next spot?
      if (!closedSet.includes(neighbor) && !neighbor.external_wall && !neighbor.internal_maze_block) 
      {
        var tempG = current.g + heuristic(neighbor, current);

        // Is this a better path than before?
        var newPath = false;
        if (openSet.includes(neighbor)) 
        {
          if (tempG < neighbor.g) 
          {
            neighbor.g = tempG;
            newPath = true;
          }
        }
        else 
        {
          neighbor.g = tempG;
          newPath = true;
          openSet.push(neighbor);
        }

        // Yes, it's a better path
        if (newPath) 
        {
          neighbor.h = heuristic(neighbor, end);
          neighbor.f = neighbor.g + neighbor.h;
          neighbor.previous = current;
        }
      }
    }
    //--- end of for loop -----------
    
  }
  // --- end still searching -----------------------------
  
  return current;
}

// ===================================================================================================================
// === Section 08. Grid Cells - create object and assign properties for each cell ====================================
// === GridCell ======================================================================================================
// ===================================================================================================================

function GridCell(i, j){
    this.i = i;
    this.j = j;
    
    this.f = 0;                                 //required to hold f(n)
    this.g = 0;                                 //required to hold g(n)
    this.h = 0;                                 //required to hold h(n)
    
    this.neighbours = [];
    this.previous = undefined;
    
    this.external_wall = false;                 //sets the specific grid cell as external wall
    this.internal_maze_block = false;           //sets the specific grid cell as maze block
    
    //add all neighbors of current node
    this.addNeighbours = function(grid){
        var i = this.i;
        var j = this.j;
        
        if (i<gridsize-1){
            this.neighbours.push(grid[i+1][j]);
        }
        
        if (i>0){
            this.neighbours.push(grid[i-1][j]);
        }
        
        if (j<gridsize-1){
            this.neighbours.push(grid[i][j+1]);
        }
        
        if (j>0){
            this.neighbours.push(grid[i][j-1]);
        }
        
        
        //addd diagonal neighbours
        if (diagonalmoves){
            if (i>0 && j>0){
                this.neighbours.push(grid[i-1][j-1]);
            }
            
            if (i<gridsize-1 && j>0){
                this.neighbours.push(grid[i+1][j-1]);
            }
            
            if(i>0 && j<gridsize-1){
                this.neighbours.push(grid[i-1][j+1]);
            }
            
            if(i<gridsize-1 && j<gridsize-1){
                this.neighbours.push(grid[i+1][j+1]);
            }
        }
    }
}


// ===================================================================================================================
// === Section 09. Conversion and Utility functions ==================================================================
// === translate, removeFromArray, heuristic =========================================================================
// ===================================================================================================================


// 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 removeFromArray(arr, ele){
    for (var i=arr.length-1; i>=0; i--){
        if (arr[i] == ele){
            arr.splice(i, 1);
        }
    }
} 

function heuristic(a,b){
    if (diagonalmoves){
        return Math.abs(a.i-b.i) + Math.abs(a.j-b.j);
    } else {
        return AB.distance2D (a.i, a.j, b.i, b.j );
    }
    //return Math.dist(a.i, a.j, b.i, b.j);
    //return Math.abs(a.i-b.i) + Math.abs(a.j-b.j);
}


// ===================================================================================================================
// === Section 10. Music and sound effects ===========================================================================
// === musicPlay, musicPause, soundAlarm =============================================================================
// ===================================================================================================================

var backmusic = AB.backgroundMusic ( MUSIC_BACK );

function musicPlay()   { backmusic.play();  }
function musicPause()  { backmusic.pause(); }

function soundAlarm()
{
	var alarm = new Audio(SOUND_ALARM);
	alarm.play();							// play once, no loop 
}

// ===================================================================================================================
// === Section 11. Event handlers ====================================================================================
// === ourKeys, keyHandler ===========================================================================================
// ===================================================================================================================

// 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 ) return true; 		// not ready yet 

   // if not one of our special keys, send it to default key handling:
	
	if ( ! ourKeys ( event ) ) return true;
	
	// 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(); return false;
}