// Cloned by Theo Delettre (21262281) on 1 Nov 2021 from World "Enemy Mind" by Theo Delettre
// Please leave this clone trail here.
// Our trapper algorithm simply calculates the best single next move by seing how the agent would react
function trapperThink(ai, aj, ei, ej, GRID, TRAPGRID){
//constants to simplify predicted agent movement
const ACTION_LEFT = 0;
const ACTION_RIGHT = 1;
const ACTION_UP = 2;
const ACTION_DOWN = 3;
const gridsize = GRID.length;
var open = [];
// We start by exploring our starting position, where the enemy currently is
open.push( new Pos(ei,ej) );
// We still have the open set, but since we are only predicting 1 move now
// we only use it to store neighboring + current pos
var curr = open[0];
if(curr.h!==0){
addNeighbors(curr.i,curr.j);
curr = maxOpen();
}
// once we have decided the best next move we can just return that
return {
nexti: curr.i ,
nextj: curr.j,
};
// For each possible next move from the agent, there are 2 moves the agent can do.
// We save the possible agent positions in the ANode class, as trees
function ANode(hai,haj,weight,parent,depth,hei,hej) {
// agent and enemy positions in this scenario
this.hai = hai;
this.haj = haj;
this.hei = hei;
this.hej = hej;
// how likely the agent is to make this move based on
this.weight = weight;
// current node depth in tree
this.depth = depth;
// parent node
this.parent = parent;
//Array of children nodes (next possible moves)
this.children = [];
// This adds the next moves to the current node's children based on the simple mind algorithm
this.getChildren = function(){
if ( this.hej < this.haj ) {
this.agentDirection(ACTION_UP, 2);
this.agentDirection(ACTION_RIGHT, 1);
this.agentDirection(ACTION_LEFT, 1);
} else if ( this.hej > this.haj ) {
this.agentDirection(ACTION_DOWN, 2);
this.agentDirection(ACTION_RIGHT, 1);
this.agentDirection(ACTION_LEFT, 1);
} else if ( this.hei < this.hai ){
this.agentDirection(ACTION_RIGHT, 2);
this.agentDirection(ACTION_UP, 1);
this.agentDirection(ACTION_DOWN, 1);
} else if ( this.hei > this.hai ) {
this.agentDirection(ACTION_LEFT, 2);
this.agentDirection(ACTION_UP, 1);
this.agentDirection(ACTION_DOWN, 1);
}
}
this.agentDirection = function(dir, weight) {
var newI = this.hai;
var newJ = this.haj;
switch(dir) {
case ACTION_LEFT:
newI--;
break;
case ACTION_RIGHT:
newI++;
break;
case ACTION_UP:
newJ++;
break;
case ACTION_DOWN:
newJ--;
break;
}
if(!occupied(newI, newJ, this.hei, this.hej))
this.children.push(new ANode(newI, newJ, weight, this, this.depth+1, this.hei, this.hej));
}
// if wew have reached a depth of 2, stop going deeper (because 2 agent moves per enemy move)
if (this.depth<2) this.getChildren();
// reccursive function to obtain average heuristic for a given setup
this.getAverage = function(){
if(this.children.length === 0) {
// if the agent has finished moving for the turn, return how close it is to a hole
//console.log(" WE GOT ",this.hai, this.haj, "FOR A HEURISTIC OF ", TRAPGRID[this.hai][this.haj]);
return TRAPGRID[this.hai][this.haj];
} else {
var totalH = 0;
var totalWeight = 0;
// if the agent still has moves left, calculate the weighted average of the next moves heuristic
for (let k = 0; k < this.children.length ; k++ ) {
totalWeight+= this.children[k].weight;
totalH += this.children[k].weight * this.children[k].getAverage();
}
return totalH / totalWeight;
}
}
}
// Position of the enemy after a move
function Pos(i,j) {
this.i = i;
this.j = j;
// heuristic is the estimated distance of the agent after it has reacted to this move
this.root = new ANode(ai,aj,1,null,0,this.i,this.j);
this.dist = Math.abs(distance2D(ai,aj,this.i,this.j) - 1);
this.h = this.root.getAverage();
}
function distance2D(ai,aj,bi,bj){
return Math.abs(ai-bi)+Math.abs(aj-bj);
}
//adds all the neighbors
function addNeighbors(x,y){
for (var a = x-1; a <= x+1 ; a++ ) {
for (var b = y-1; b <= y+1 ; b++ ) {
//if(x >=0 && y>=0 && x<gridsize && y<gridsize) { //Not necessary since there should be a wall outline
if(GRID[a][b] != 1 && GRID[a][b] != 2){
open.push( new Pos(a,b) );
}
}
}
}
// Pick the move with the best heuristic. If multiple moves have the same heuristic, pick the that brings enemy closest to agent
function maxOpen(){
var maxPos = null;
console.log("----- POSSIBLE MOVES FROM", ei, ej, " ------");
for ( i = 0; i < open.length ; i++ ) {
console.log(" E Coord:",open[i].i,open[i].j, "A coord:",ai,aj, "heuristic:",open[i].h, "Dist:", open[i].dist);
if(maxPos===null){
maxPos = open[i];
} else if (open[i].h < maxPos.h || (open[i].h == maxPos.h && open[i].dist < maxPos.dist) ){
maxPos = open[i];
}
}
console.log(" CHOSEN E Coord:",maxPos.i,maxPos.j, "A coord:",ai,aj, "heuristic:",maxPos.h, "Dist:", maxPos.dist);
return maxPos;
}
function occupied(i,j,hei,hej) {
if (GRID[i][j] == 1 || GRID[i][j] == 2) return true;
if (i == hei && j == hej) return true;
return false;
}
}