Code viewer for Mind: Smart Prey
// Sanchit Shreepraksh Akhauri 20210205
//Agent Mind



// Cloned by Sanchit Akhauri on 20 Nov 2020 from Mind "Complex Mind" by Starter user 
// Please leave this clone trail here.
 



// =================================================================================================
// Sample Mind for more complex starter World  
// =================================================================================================

// World tells us agent position and enemy position
// World does not tell us of existence of walls
// if return invalid move (not empty square) World just ignores it and we miss a turn 

AB.mind.newRun = function()                  
{
};

AB.mind.endRun = function()                 
{
};


var agentIntelligent = true; // Samchit : -Defines Agent as smart or simple

// Sanchit : -Stores position of co-ordinates which are closed
var agentclosedList = new Array(gridsize);

// Sanchit : - Stores position of co-ordinates which are open to access and not in closed list.
var agentopenList = {};

// Sanchit : -  A Utility Function to check whether destination cell has 
// been reached or not 
function isAgentDestination(row, col) {
    if (row == targeti && col == targetj)
        return (true);
    else
        return (false);
}

// Sanchit : - A Utility Function to trace the path from the source 
// to destination 

var agentfinalpath = [];
function traceAgentPath(agentcellDetails) {
    agentfinalpath = [];
    var row = targeti;
    var col = targetj;
    var Path = [];
    var temp = "";

    //Stores the co-orinates of parents from distination to source in path variable 
    while (!(agentcellDetails[row][col].parent_i == row
        && agentcellDetails[row][col].parent_j == col)) {
        Path.push(new pair(row, col));
        var temp_row = agentcellDetails[row][col].parent_i;
        var temp_col = agentcellDetails[row][col].parent_j;
        row = temp_row;
        col = temp_col;
        temp = temp + " ( "+temp_row+" "+temp_col+" )";
    }

    Path.push(new pair(row, col));
    
    // Stores values from path variable to finalpath variable in the order from enemy to agent
    while (Path.length > 0){
        agentfinalpath.push(Path.pop());
    }
}

function updateAgentOpenList(i, j, agentopen_i, agentopen_j, agentcellDetails) { // Sanchit : - this function updates the value of successor in openList

    // To store the 'g', 'h' and 'f' of the 8 successors 
    var gNew, hNew, fNew;
    var foundDest =false; // Flag to inform if the destination is found

    if (isValid(i, j)) {
        // If the destination cell is the same as the 
        // current successor 
        if (isAgentDestination(i, j, destination)) {
            // Set the Parent of the destination cell 
            agentcellDetails[i][j].parent_i = agentopen_i;
            agentcellDetails[i][j].parent_j = agentopen_j;
            traceAgentPath(agentcellDetails, destination);
            foundDest = true;
        }
        // If the successor is already on the closed 
        // list or if it is blocked, then ignore it. 
        // Else do the following 
        else if (closedList[i][j] == false &&
            !occupied(i, j)) {
            gNew = agentcellDetails[agentopen_i][agentopen_j].g + 1.0;
            hNew = heuristicAgent(i, j);
            fNew = gNew + hNew;

            if (agentcellDetails[i][j].f == Number.MAX_VALUE ||
                agentcellDetails[i][j].f > fNew) {
                agentopenList[i + " " + j] = new pPair(fNew, i, j);

                // Update the details of this cell 
                agentcellDetails[i][j].f = fNew;
                agentcellDetails[i][j].g = gNew;
                agentcellDetails[i][j].h = hNew;
                agentcellDetails[i][j].parent_i = agentopen_i;
                agentcellDetails[i][j].parent_j = agentopen_j;
            }
        }
    }
    return foundDest;
}


AB.mind.getAction = function ( x )		// x is an array of [ ai, aj, ei, ej ]
{   
    agentIntelligent = true;
    var ai = parseInt(x[0]);
    var aj = parseInt(x[1]);
    var tempi = ai;
    var tempj = aj;

  // Store co-ordinates of the agent
  destination.i = ai;
  destination.j = aj;

  // Stores position of co-ordinates which are closed
  agentclosedList = Array.from({ length: gridsize }, () => Array.from({ length: gridsize }, () => false));

  //Store details of each square
  var agentcellDetails = new Array(gridsize);
  agentcellDetails = Array.from({ length: gridsize }, () => new Array(gridsize));
  agentcellDetails = Array.from({ length: gridsize }, () => Array.from({ length: gridsize }, () =>
      new Details(Number.MAX_VALUE, Number.MAX_VALUE, Number.MAX_VALUE, -1, -1)));

  //Update agentcellDetails value of agent starting point
  var i = destination.i;
  var j = destination.j;
  agentcellDetails[i][j].f = 0.0;
  agentcellDetails[i][j].g = 0.0;
  agentcellDetails[i][j].h = 0.0;
  agentcellDetails[i][j].parent_i = i;
  agentcellDetails[i][j].parent_j = j;

  //Store f(n) and co-ordinates value in agentopenList
  agentopenList = {};
  agentopenList[i + " " + j] = (new pPair(0.0, i, j));

  while (Object.keys(agentopenList).length > 0) { // While tere is value in agentopenList run the loop

      //Pop the first value in agentopenList
      var firstKey = Object.keys(agentopenList)[0];
      var p = agentopenList[firstKey];
      delete agentopenList[firstKey];
   
      // Add this vertex to the closed list 
      agentopen_i = p.set.i;
      agentopen_j = p.set.j;
      agentclosedList[agentopen_i][agentopen_j] = true;

      // Update agentopenList value with successor co-ordinates which are valid, break if the target is found
      if (updateAgentOpenList(agentopen_i + 1, agentopen_j, agentopen_i, agentopen_j, agentcellDetails)) break;
      if (updateAgentOpenList(agentopen_i, agentopen_j + 1, agentopen_i, agentopen_j, agentcellDetails)) break;
      if (updateAgentOpenList(agentopen_i - 1, agentopen_j, agentopen_i, agentopen_j, agentcellDetails)) break;
      if (updateAgentOpenList(agentopen_i, agentopen_j - 1, agentopen_i, agentopen_j, agentcellDetails)) break;
  }

  if (agentfinalpath.length > 2) {         // If finalpath's length is greater than 2 then the value in 1 index contains the next step.
      var bestPositionAgent = agentfinalpath[1];
      if(!occupied(bestPositionAgent.i,bestPositionAgent.j)){
        ai = bestPositionAgent.i;
        aj = bestPositionAgent.j
      }
  }
  else{   // If final path is zero then no possible path is possible from the agent to the target
      changeTargetValue(); // Change the target in this case
      return  ( AB.randomIntAtoB (0,3) ); // Return random movement
  }

  setupAgentLine(agentfinalpath); // calls setupAgentLine function

  // Returns ndirection to next step depending on value from finalPath
  if ( aj > tempj  ) 	return ACTION_UP;  
  if ( aj < tempj ) 	return ACTION_DOWN; 

  if (ai > tempi ) 	return ACTION_RIGHT;  
  if ( ai < tempi ) 	return ACTION_LEFT; 

  return  ( AB.randomIntAtoB (0,3) );
    
 };
 

// Heuristic evaluation function 
function heuristicAgent(ai,aj){

    // return Manhattan distance
    axisj_distance = aj - targetj;
    axisi_distance = ai - targeti;
    var result = Math.abs(axisi_distance) + Math.abs(axisj_distance);
    return result   
}

function setupAgentLine(finalPath) {   // Sanchit : - This function draws the A* path from agent to the target every time agent takes a step  
    const material = new THREE.LineBasicMaterial( { color: 0x0000FF } );
    
    const points = [];
    
    for(var i=1;i<finalPath.length;i++)
        points.push( translate(finalPath[i].i, finalPath[i].j));
    
    const geometry = new THREE.BufferGeometry().setFromPoints( points );
    const line = new THREE.Line( geometry, material );
    ABWorld.scene.add(line);
    setTimeout(() => {
        geometry.dispose();
        material.dispose();
        ABWorld.scene.remove(line);
        },AB.clockTick);
}