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