Code viewer for World: Enemy Mind
// Created by Theo Delettre (21262281) on 27 Oct 2021

function enemyThink(ai, aj, ei, ej, GRID){
    const gridsize = GRID.length;
    
    // The open set of positions left to visit
    var open = [];
    
    //AMAP serves as a way to add info to seen grid coords and so also serves as 
    // the "closed" set
    var AMAP = new Array(gridsize);
    for ( i = 0; i < gridsize ; i++ ) {
        AMAP[i] = new Array(gridsize);
        for ( j = 0; j < gridsize ; j++ ) {
	        AMAP[i][j] = null;
	    }
    }
    
    // We start by exploring our starting position, where the enemy currently is
    //  this also addsw it to our open set.
    addPos(ei,ej,0,null);
    var curr = open[0];
    
    // As long as we haven't reached our objective or ran out of unvisited points we run the algorithm
    while(curr !== null){
        // if heuristics is 0, we reached our objective and can stop searching
        if(curr.h===0){
            curr.isClosed = true;
            break;
        } else {
            // We identify all the neighboring positions and their properties
            //  & add them to the open set
            addNeighbors(curr.i,curr.j,curr.g);
            
            //The current position has now been closed and we can close it
            curr.isClosed = true;
            open.splice(open.indexOf(curr), 1);
            
            //restart the process but with the next best position (F) in the open set
            curr = maxOpen();
        }
    }
    
    // Once we found the path (or lack thereof), we send the info back to the world
    if(curr!==null){
        //Important to send the last position to find the whole path (through previous)
        var lastcurr = curr;
        
        // we get the values of the first step in our path
        if(curr.prev !== null)
        {
            while(curr.prev.prev!==null){
                curr = curr.prev;
            }
        }
        return {
            nexti:curr.i ,
            nextj: curr.j,
            AMAP: AMAP,
            last: lastcurr,
        };
    } else {
        return {
            nexti:ei ,
            nextj: ej,
            AMAP: AMAP,
            last: null,
        };
    }
    
    // This is our Pos class, each found coordinate in AMAP contains an instance of this
    function Pos(i,j,g,prev) {
        //coordinates
        this.i = i;
        this.j = j;
        
        // g, heuristic, and f values
        this.g = g;
        // Our heuristic is distance to the agent ( h = number of steps for the best case scenario, < otherwise )
        // The -1 is there because the objective is actually being in an adjacent square to the agent (we cannot move on top of agent)
        this.h = Math.abs(distance2D(ai,aj,this.i,this.j) - 1);
        this.f = this.g + this.h;
        
        // We start in the open set
        this.isClosed = false;
        
        // The Pos object we came from. Important to identify final path
        this.prev = prev;
        
        // If this position can be reached with a better path, we replace its values and origin
        this.update = function(newG, from) {
            if(newG < this.g){
                this.g = newG;
                this.f = this.g + this.h;
                this.prev = from;
            }
        }
    }
    
    function distance2D(ai,aj,bi,bj){
        return Math.abs(bi-ai)+Math.abs(bj-aj);
    }
    
    // Adds information to the given coordinate and adds them to the open set,
    // or tries to update them if they are already known but unvisited
    function addPos(i,j,newG,from){
        if(AMAP[i][j]===null){
            AMAP[i][j] = new Pos(i,j,newG,from);
            open.push(AMAP[i][j]);
        } else if(!AMAP[i][j].isClosed){
            AMAP[i][j].update(newG,from);
        }
    }
    
    // Identify all neighboring positions and adds them to the open set 
    function addNeighbors(x,y,currG){
        for ( i = x-1; i <= x+1 ; i++ ) {
            for ( j = y-1; j <= y+1 ; j++ ) {
                //if(x >=0 && y>=0 && x<gridsize && y<gridsize) { //Not necessary since there should be a wall outline
                
                if(GRID[i][j] != 1 && GRID[i][j] != 2 && !(x==i && y==j) && (x!= ai || y!=aj)){
                    addPos(i,j, currG + distance2D(x,y,i,j), AMAP[x][y])
                }
    	    }
        }
    }
    
    //finds the Pos with minimum F in the open set and returns in
    function maxOpen(){
        var maxPos = null;
        for ( i = 0; i < open.length ; i++ ) {
            if(maxPos===null){
                maxPos = open[i];
            } else if (open[i].f < maxPos.f){
                maxPos = open[i];
            }
        }
        return maxPos;
    }
}