Code viewer for Mind: A* Andross Mind

// Cloned by Philip on 24 Nov 2020 from Mind "Complex Mind (clone by Philip)" by Philip 
// 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 


var openSet = [];
var closedSet = [];
var map = null;
var targetSpot = null;
var result = null;

AB.mind.getAction = function (x) //This is fed by the get state method		// x is an array of [ ai, aj, ei, ej ]
{
    var ai = x[0];
    var aj = x[1];
    var ei = x[2];
    var ej = x[3];
    map = x[4];
    intialiseParameters();
    getEnemy();
    getAgent();
    return result;
};

function getEnemy(){
    try{
    initiateSearch([ei, ej], [ai, aj])

    // if strictly move away, will get stuck at wall, so introduce randomness 

    if (result.enemy.nextSquare.j > ej) result.enemy.action = ACTION_UP;
    if (result.enemy.nextSquare.j < ej) result.enemy.action = ACTION_DOWN;

    if (result.enemy.nextSquare.i > ei) result.enemy.action = ACTION_RIGHT;
    if (result.enemy.nextSquare.i < ei) result.enemy.action = ACTION_LEFT;
    }
    catch(error){
        console.log("pathNot found")
    }
}

function getAgent(){
if ( ej < aj ) 	result.agent.action = ( AB.randomPick ( ACTION_UP,		AB.randomPick(ACTION_RIGHT,ACTION_LEFT) 	)); 
if ( ej > aj ) 	result.agent.action = ( AB.randomPick ( ACTION_DOWN,	AB.randomPick(ACTION_RIGHT,ACTION_LEFT) 	)); 

if ( ei < ai ) 	result.agent.action = ( AB.randomPick ( ACTION_RIGHT,	AB.randomPick(ACTION_UP,ACTION_DOWN) 		)); 
if ( ei > ai ) 	result.agent.action = ( AB.randomPick ( ACTION_LEFT,	AB.randomPick(ACTION_UP,ACTION_DOWN) 		)); 
}


// Function to delete element from the array
function removeFromArray(arr, elt) {
    // Could use indexOf here instead to be more efficient
    for (var i = arr.length - 1; i >= 0; i--) {
        if (arr[i].i == elt.i && arr[i].j == elt.j)
            arr.splice(i, 1);
    }
}

function initiateSearch(intialCoords, targetCoords) {
    var initialSpot = new Space(intialCoords[0], intialCoords[1]);
    initialSpot.g = 0;
    targetSpot = new Space(targetCoords[0], targetCoords[1]);

    openSet.push(initialSpot);
    var winnerIndex = 0;
    var winningPath = findPath(initialSpot, winnerIndex);
    return winningPath;
}

//Need to add a no avaialable path clause but not sure what that looks like
function findPath(current, winningIndex) {

    for (var i = 0; i < openSet.length; i++)
        if (openSet[i].f < openSet[winningIndex].f)
            winningIndex = i;

    current = openSet[winningIndex];

    if(!current) {
        console.log("failure - enemy path not foud");
        return null;
    }
    
    if (current.i == targetSpot.i && current.j == targetSpot.j) {
        console.log("success - found path");
        return findNextMove(current, null);
    }
    else {

        removeFromArray(openSet, current);
        closedSet.push(current);

        current.addNeighbours();
        current.neighbours.forEach(neighbour => {

            if (!neighbour.wall && !closedSet.includes(neighbour)) {
                var tempG = current.g + heuristic(current, neighbour);
                var newPath = false;

                if (!neighbour.g || tempG < neighbour.g) {
                    neighbour.g = tempG;
                    newPath = true;
                }

                if (!ArrayIncludesSpot(openSet, neighbour))
                    openSet.push(neighbour);

                // Yes, it's a better path
                if (newPath) {
                    neighbour.h = heuristic(neighbour, targetSpot);
                    neighbour.f = neighbour.g + neighbour.h;
                    neighbour.parent = current;
                }
            }
        });

        //Time to recurse
        var output = findPath(current, winningIndex);
        return output;
    }
}

function findNextMove(current, child) {

    if (current.parent) { //If there is a parent, go to it and then check if it has a parent and so on
        result.enemy.path.unshift(current.parent);
        findNextMove(current.parent, current);
    }
    else
        result.enemy.nextSquare = child; //if this doesn't have a parent, its where we started, so this is where we need to go
}

//Lets reset the parameters at the start of each run to make sure the objects are properly formed.
function intialiseParameters() {
    openSet = [];
    closedSet = [];
    targetSpot = null;
    result = new Entities();
}


//It'll be easier to use an object to handle each location
function Space(i, j) {
    this.i = i;
    this.j = j;
    this.wall = (map[i][j] !== 0);

    this.f = 0;
    this.g = null; //Remember to do a null check where this can be encountered as a null
    this.h = 0;
    this.neighbours = [];
    this.parent = undefined;

    this.addNeighbours = function () {
        var rows = map.length;
        var columns = map[0].length;
        var i = this.i;
        var j = this.j;

        if (i < columns - 1) this.neighbours.push(new Space(i + 1, j));
        if (i > 0) this.neighbours.push(new Space(i - 1, j));
        if (j < rows - 1) this.neighbours.push(new Space(i, j + 1));
        if (j > 0) this.neighbours.push(new Space(i, j - 1));

    }
}

function Entity() {
    this.nextSquare = null;
    this.path = [];
    this.action = null;
}

//This is going to hold the data of all the entities that have been added
function Entities() {
    this.enemy = new Entity();
    this.agent = new Entity();
}


function ArrayIncludesSpot(arr, x) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].i == x.i && arr[i].j == x.j)
            return true
    }
    return false;
}

//Centralise the hueristic control so its easily changed
function heuristic(a, b) {
    //While I'm working on the algorithm, set this to something that will easily show movement
    return (Math.abs(a.i - b.i) + Math.abs(a.j - b.j));

}