|
|
@@ -13,10 +13,10 @@ package gr.auth.ee.dsproject.node; |
|
|
|
//import java.awt.image.PackedColorModel;
|
|
|
|
import gr.auth.ee.dsproject.pacman.PacmanUtilities;
|
|
|
|
import gr.auth.ee.dsproject.pacman.Room;
|
|
|
|
import gr.auth.ee.dsproject.node.Vector;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @class Node
|
|
|
|
* @class Node89978445
|
|
|
|
* @brief
|
|
|
|
* This class holds each move of the current round and it's evaluation
|
|
|
|
*
|
|
|
@@ -28,43 +28,16 @@ public class Node89978445 |
|
|
|
* This is used also as the "return status" of the object
|
|
|
|
*/
|
|
|
|
int nodeMove; // Pacman's current move
|
|
|
|
int nodeX; // Pacman's current x coordinate
|
|
|
|
int nodeY; // Pacman's current y coordinate
|
|
|
|
int newX; // Pacman's new x coordinate
|
|
|
|
int newY; // Pacman's new y coordinate
|
|
|
|
int[] nodeXY; // Pacman's current x,y coordinate
|
|
|
|
int[] newXY; // Pacman's new x,y coordinate
|
|
|
|
int[][] currentGhostPos; // Array holding the Ghost (x,y) pairs
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int[][] flagPos; // Array holding the flag (x,y) pairs
|
|
|
|
boolean[] currentFlagStatus; // Array holding the flag captuder status
|
|
|
|
boolean[] currentFlagStatus; // Array holding the flag capture status
|
|
|
|
Room[][] Maze; // copy of the current Maze
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ======== evaluation data ===========
|
|
|
|
*/
|
|
|
|
Vector[] flagCurVectors; /**<
|
|
|
|
* Array holding the free vectors from current Pacman's position
|
|
|
|
* to Flags
|
|
|
|
* @note
|
|
|
|
* in case of captured flag the corresponding vector is [0, 0]
|
|
|
|
*/
|
|
|
|
Vector[] ghostCurVectors; /**<
|
|
|
|
* Array holding the free vectors from current Pacman's position
|
|
|
|
* to ghosts
|
|
|
|
*/
|
|
|
|
Vector[] ghostNextVectors; /**<
|
|
|
|
* Array holding the free vectors from next Pacman's position
|
|
|
|
* to ghosts. This next position is the one is been evaluated.
|
|
|
|
*/
|
|
|
|
double [] ghostNorms; //*< Helping array holding the norms of the ghostNextVectors vectors
|
|
|
|
double [][] flagDots; /**<
|
|
|
|
* 2d array holding the normalized dot products of all the compinations
|
|
|
|
* in flagCurVectors and ghostCurVectors vectors
|
|
|
|
* rows represent the flags and columns the ghosts
|
|
|
|
*/
|
|
|
|
|
|
|
|
double goal, life; // evaluation factors
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============ Constructors ==============
|
|
|
@@ -74,62 +47,44 @@ public class Node89978445 |
|
|
|
* The simple constructor. Just initialize the data
|
|
|
|
* @note
|
|
|
|
* Using this constructor means that the user MUST call setMaze(), setPosition()
|
|
|
|
* and setMove manually after the creation of the Nodexxxx object
|
|
|
|
* and setMove manually after the creation of the Node89978445 object
|
|
|
|
*/
|
|
|
|
public Node89978445 ()
|
|
|
|
{
|
|
|
|
// Fill members
|
|
|
|
nodeX = newX = Globals.NO_PLACE;
|
|
|
|
nodeY = newY = Globals.NO_PLACE;
|
|
|
|
nodeXY = newXY = Globals.FALSE_POS;
|
|
|
|
nodeMove = Globals.INVALID_MOVE;
|
|
|
|
nodeEvaluation = Globals.NO_EVAL;
|
|
|
|
|
|
|
|
//calculate members
|
|
|
|
//newX += (nodeMove == Room.SOUTH) ? 1:0;
|
|
|
|
//newX -= (nodeMove == Room.NORTH) ? 1:0;
|
|
|
|
//newY += (nodeMove == Room.EAST) ? 1:0;
|
|
|
|
//newY -= (nodeMove == Room.WEST) ? 1:0;
|
|
|
|
|
|
|
|
// allocate objects
|
|
|
|
currentGhostPos = new int [PacmanUtilities.numberOfGhosts][2];
|
|
|
|
flagPos = new int [PacmanUtilities.numberOfFlags][2];
|
|
|
|
currentFlagStatus = new boolean[PacmanUtilities.numberOfFlags];
|
|
|
|
|
|
|
|
//evaluation data init
|
|
|
|
ghostNorms = new double [PacmanUtilities.numberOfGhosts];
|
|
|
|
flagDots = new double [PacmanUtilities.numberOfFlags][PacmanUtilities.numberOfGhosts];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Constructor for the node
|
|
|
|
* Constructor for the Node89978445
|
|
|
|
* @param Maze The current maze object
|
|
|
|
* @param curX The current pacman's x position
|
|
|
|
* @param curY The current pacman's y position
|
|
|
|
* @param curXY The current pacman's (x, y) position
|
|
|
|
* @param move The move under inspection
|
|
|
|
*/
|
|
|
|
public Node89978445 (Room[][] Maze, int curX, int curY, int move)
|
|
|
|
public Node89978445 (Room[][] Maze, int[] curXY, int move)
|
|
|
|
{
|
|
|
|
this.Maze = Maze; // Fill members
|
|
|
|
nodeX = newX = curX;
|
|
|
|
nodeY = newY = curY;
|
|
|
|
nodeXY = newXY = curXY;
|
|
|
|
nodeMove = move;
|
|
|
|
nodeEvaluation = Globals.NO_EVAL;
|
|
|
|
|
|
|
|
|
|
|
|
//calculate members
|
|
|
|
newX += (nodeMove == Room.SOUTH) ? 1:0;
|
|
|
|
newX -= (nodeMove == Room.NORTH) ? 1:0;
|
|
|
|
newY += (nodeMove == Room.EAST) ? 1:0;
|
|
|
|
newY -= (nodeMove == Room.WEST) ? 1:0;
|
|
|
|
newXY = pacmanValidMove (nodeXY, move);
|
|
|
|
|
|
|
|
// allocate objects
|
|
|
|
currentGhostPos = new int [PacmanUtilities.numberOfGhosts][2];
|
|
|
|
flagPos = new int [PacmanUtilities.numberOfFlags][2];
|
|
|
|
currentFlagStatus = new boolean[PacmanUtilities.numberOfFlags];
|
|
|
|
|
|
|
|
//evaluation data init
|
|
|
|
ghostNorms = new double [PacmanUtilities.numberOfGhosts];
|
|
|
|
flagDots = new double [PacmanUtilities.numberOfFlags][PacmanUtilities.numberOfGhosts];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
@@ -145,12 +100,12 @@ public class Node89978445 |
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set pacman's position
|
|
|
|
* @param curX Pacman's current X position
|
|
|
|
* @param curY Pacman's current Y position
|
|
|
|
* @param curXY Pacman's current X, Y position
|
|
|
|
*/
|
|
|
|
public void setPosition (int curX, int curY) {
|
|
|
|
nodeX = curX;
|
|
|
|
nodeY = curY;
|
|
|
|
public void setPosition (int[] curXY) {
|
|
|
|
nodeXY = curXY;
|
|
|
|
//update members
|
|
|
|
newXY = pacmanValidMove (curXY, nodeMove);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
@@ -159,12 +114,8 @@ public class Node89978445 |
|
|
|
*/
|
|
|
|
public void setMove (int move) {
|
|
|
|
nodeMove = move;
|
|
|
|
|
|
|
|
//update members
|
|
|
|
newX += (nodeMove == Room.SOUTH) ? 1:0;
|
|
|
|
newX -= (nodeMove == Room.NORTH) ? 1:0;
|
|
|
|
newY += (nodeMove == Room.EAST) ? 1:0;
|
|
|
|
newY -= (nodeMove == Room.WEST) ? 1:0;
|
|
|
|
newXY = pacmanValidMove (nodeXY, move);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
@@ -183,7 +134,7 @@ public class Node89978445 |
|
|
|
currentFlagStatus = checkFlags ();
|
|
|
|
|
|
|
|
// validation and evaluate move
|
|
|
|
if (isValidMove ()) {
|
|
|
|
if (newXY != Globals.FALSE_POS) {
|
|
|
|
// If already evaluated do not re-evaluate the move
|
|
|
|
if (nodeEvaluation == Globals.NO_EVAL)
|
|
|
|
nodeEvaluation = evaluate ();
|
|
|
@@ -196,7 +147,7 @@ public class Node89978445 |
|
|
|
|
|
|
|
/**
|
|
|
|
* @return
|
|
|
|
* The current move of the Nodexxxxxxx
|
|
|
|
* The current move of the Node89978445
|
|
|
|
*/
|
|
|
|
public int getMove () {
|
|
|
|
return nodeMove;
|
|
|
@@ -284,216 +235,292 @@ public class Node89978445 |
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Check if the requested move is valid
|
|
|
|
*/
|
|
|
|
private boolean isValidMove ()
|
|
|
|
{
|
|
|
|
boolean status = true; //have faith
|
|
|
|
|
|
|
|
// filters for the move
|
|
|
|
if (Maze[nodeX][nodeY].walls[nodeMove] == 0)
|
|
|
|
status = false;
|
|
|
|
// keep filling the filters [if any]
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============ evaluation helper methods ==============
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* return the average valued of the array
|
|
|
|
* @param x The array to calculate
|
|
|
|
* @param size The size of the array
|
|
|
|
* @return The average value
|
|
|
|
* @param creature creature's (x,y)
|
|
|
|
* @return
|
|
|
|
* Return true if the creature is inside the maze boxes
|
|
|
|
*/
|
|
|
|
private double aver (double [] x, int size)
|
|
|
|
private boolean isInsideBox (int[] creature)
|
|
|
|
{
|
|
|
|
double acc = 0; // accumulator
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
acc += x[i];
|
|
|
|
boolean ret = false; //have faith
|
|
|
|
for (int i=0 ; i<4 ; ++i) {
|
|
|
|
if ((creature[0]>=Globals.BOXES[i][0][0] && creature[0]<=Globals.BOXES[i][1][0]) &&
|
|
|
|
(creature[1]>=Globals.BOXES[i][0][1] && creature[1]<=Globals.BOXES[i][1][1]))
|
|
|
|
ret = true;
|
|
|
|
}
|
|
|
|
return acc/size;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* return the minimum valued item of the array
|
|
|
|
* @param x The array to filter
|
|
|
|
* @param size The size of the array
|
|
|
|
* @return The minimum value
|
|
|
|
*/
|
|
|
|
private double min (double [] x, int size)
|
|
|
|
private int[] ghostValidMove (int[] ghost, int move)
|
|
|
|
{
|
|
|
|
double ret = 0;
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
if (i==0 || ret > x[i])
|
|
|
|
ret = x[i];
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
int[] newPos = new int[2];
|
|
|
|
|
|
|
|
// find hypothetical new position
|
|
|
|
newPos[0] = ghost[0];
|
|
|
|
newPos[1] = ghost[1];
|
|
|
|
newPos[0] += (move == Room.SOUTH) ? 1:0;
|
|
|
|
newPos[0] -= (move == Room.NORTH) ? 1:0;
|
|
|
|
newPos[1] += (move == Room.EAST) ? 1:0;
|
|
|
|
newPos[1] -= (move == Room.WEST) ? 1:0;
|
|
|
|
|
|
|
|
// Valid filters
|
|
|
|
if (!((newPos[0] >= 0 && newPos[0] < PacmanUtilities.numberOfRows) &&
|
|
|
|
(newPos[1] >= 0 && newPos[1] < PacmanUtilities.numberOfColumns)))
|
|
|
|
return Globals.FALSE_POS;
|
|
|
|
if (!isInsideBox(ghost) && (Maze[ghost[0]][ghost[1]].walls[move] == 0))
|
|
|
|
return Globals.FALSE_POS;
|
|
|
|
return newPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* return the maximum valued item of the array
|
|
|
|
* @param x The array to filter
|
|
|
|
* @param size The size of the array
|
|
|
|
* @return The maximum value
|
|
|
|
* Check if the requested move is valid
|
|
|
|
*/
|
|
|
|
private double max (double [] x, int size) {
|
|
|
|
double ret = 0;
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
if (i==0 || ret<x[i])
|
|
|
|
ret = x[i];
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
private int[] pacmanValidMove (int[] pacman, int move)
|
|
|
|
{
|
|
|
|
int[] newPos = new int[2];
|
|
|
|
|
|
|
|
// find hypothetical new position
|
|
|
|
newPos[0] = pacman[0];
|
|
|
|
newPos[1] = pacman[1];
|
|
|
|
newPos[0] += (move == Room.SOUTH) ? 1:0;
|
|
|
|
newPos[0] -= (move == Room.NORTH) ? 1:0;
|
|
|
|
newPos[1] += (move == Room.EAST) ? 1:0;
|
|
|
|
newPos[1] -= (move == Room.WEST) ? 1:0;
|
|
|
|
|
|
|
|
// Pacman curves Maze plane to a Torus
|
|
|
|
if (newPos[0] < 0) newPos[0] = PacmanUtilities.numberOfRows;
|
|
|
|
if (newPos[0] >= PacmanUtilities.numberOfRows ) newPos[0] = 0;
|
|
|
|
if (newPos[1] < 0) newPos[1] = PacmanUtilities.numberOfColumns;
|
|
|
|
if (newPos[1] >= PacmanUtilities.numberOfColumns ) newPos[1] = 0;
|
|
|
|
|
|
|
|
// Valid filters
|
|
|
|
if (!isInsideBox(pacman) && (Maze[pacman[0]][pacman[1]].walls[move] == 0))
|
|
|
|
return Globals.FALSE_POS;
|
|
|
|
return newPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* return the best flag index which is the minimum
|
|
|
|
* valued item of the array
|
|
|
|
* @param x The array to filter
|
|
|
|
* @param size The size of the array
|
|
|
|
* @return The minimum value
|
|
|
|
* A Breadth-first search to find the shortest path distance
|
|
|
|
* from a ghost to a point in the maze
|
|
|
|
* @param ghost Ghost (x, y)
|
|
|
|
* @param xy The x, y coordinate in Maze
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private int bestFlagPosition (double [] x, int size) {
|
|
|
|
int pos = 0;
|
|
|
|
double min = 9999999; // Start large enough
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
if ((min > x[i]) && (x[i] != 0)) {
|
|
|
|
/*<
|
|
|
|
* here we exclude the zero vectors from been candidates
|
|
|
|
* as these vectors represent the captured flags
|
|
|
|
*/
|
|
|
|
min = x[i];
|
|
|
|
pos = i;
|
|
|
|
private int ghostMoveDist (int[] ghost, int[] xy)
|
|
|
|
{
|
|
|
|
int move;
|
|
|
|
int steps, qStepItems; // distance and group counters
|
|
|
|
boolean done = false; // algo ending flag
|
|
|
|
int r = PacmanUtilities.numberOfRows; // helper for shorting names
|
|
|
|
int c = PacmanUtilities.numberOfColumns; // helper for shorting names
|
|
|
|
int[] xyItem = new int [2]; // Coordinates of the current position of the algo
|
|
|
|
int[] xyNext = new int [2]; // Coordinates of the next valid position of the algo
|
|
|
|
Queue2D q = new Queue2D (r+c - 1); // Queue to feed with possible position
|
|
|
|
int [][] dist = new int [r][c];
|
|
|
|
/*
|
|
|
|
* 2D array holding all the distances from the ghost to each square of the maze
|
|
|
|
*/
|
|
|
|
|
|
|
|
// If target square is inside a box abort with max distance
|
|
|
|
if (isInsideBox(xy))
|
|
|
|
return Globals.MAX_DISTANCE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* init data for algorithm
|
|
|
|
*/
|
|
|
|
for (int i=0 ; i<r ; ++i) {
|
|
|
|
for (int j=0 ; j<c ; ++j) {
|
|
|
|
dist[i][j] = -1;
|
|
|
|
// dist array starts with -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Creates the @ref flagCurVectors
|
|
|
|
* these vectors are free vectors from current Pacman's position
|
|
|
|
* to Flags
|
|
|
|
* @note
|
|
|
|
* in case of captured flag the corresponding vector is [0, 0]
|
|
|
|
* @return the array holding the vectors
|
|
|
|
*/
|
|
|
|
private Vector[] makeFlagCurVectors () {
|
|
|
|
Vector[] ret = new Vector[PacmanUtilities.numberOfFlags];
|
|
|
|
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfFlags ; ++i) {
|
|
|
|
if (currentFlagStatus[i] == true)
|
|
|
|
ret[i] = new Vector (); // eliminate the capture vectors
|
|
|
|
else
|
|
|
|
ret[i] = new Vector (nodeX, nodeY, flagPos[i][0], flagPos[i][1]);
|
|
|
|
// loop data
|
|
|
|
dist [ghost[0]][ghost[1]] = 0; //starting point is the ghost position
|
|
|
|
q.insert (ghost); // feed first loop data
|
|
|
|
qStepItems = 1; // init counters
|
|
|
|
steps = 1;
|
|
|
|
/*
|
|
|
|
* Main loop of the algorithm
|
|
|
|
*
|
|
|
|
* For every position we check the valid moves, we apply to them
|
|
|
|
* the step number and we push them to queue. We group the items in
|
|
|
|
* queue with their step number by measuring how many we push them for
|
|
|
|
* the current steps variable value.
|
|
|
|
*/
|
|
|
|
while (done == false) {
|
|
|
|
if (qStepItems>0) { // Have we any item on the current group?
|
|
|
|
xyItem = q.remove (); //load an item of the current group
|
|
|
|
--qStepItems; // mark the removal of the item
|
|
|
|
for (move=0 ; move<4 ; ++move) {
|
|
|
|
// loop every valid next position for the current position
|
|
|
|
if ((xyNext = ghostValidMove (xyItem, move)) != Globals.FALSE_POS) {
|
|
|
|
if (dist[xyNext[0]][xyNext[1]] == -1) {
|
|
|
|
// If we haven't been there
|
|
|
|
dist[xyNext[0]][xyNext[1]] = steps; // mark the distance
|
|
|
|
if ((xyNext[0] == xy[0]) && (xyNext[1] == xy[1])) {
|
|
|
|
/*
|
|
|
|
* first match:
|
|
|
|
* The first time we reach destination we have count the
|
|
|
|
* distance. No need any other try
|
|
|
|
*/
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// If we are not there yet, feed queue with another position
|
|
|
|
q.insert (xyNext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// We are done with group, mark how many are for the next one
|
|
|
|
if ((qStepItems = q.size()) <= 0)
|
|
|
|
return dist[xy[0]][xy[1]]; // fail safe return
|
|
|
|
++steps; // Update distance counter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
return dist[xy[0]][xy[1]];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Creates the @ref ghostCurVectors
|
|
|
|
* these vectors are free vectors from current Pacman's position
|
|
|
|
* to ghosts
|
|
|
|
* @return the array holding the vectors
|
|
|
|
* A Breadth-first search to find the shortest path distance
|
|
|
|
* from the pacman to a point in the maze. The point will normaly
|
|
|
|
* represent a flag
|
|
|
|
* @param pacman Pacman's (x, y)
|
|
|
|
* @param xy The x, y coordinate in Maze
|
|
|
|
* @return
|
|
|
|
*/
|
|
|
|
private Vector[] makeGhostCurVectors () {
|
|
|
|
Vector [] ret = new Vector[PacmanUtilities.numberOfGhosts];
|
|
|
|
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
|
|
|
|
ret[i] = new Vector (nodeX, nodeY, currentGhostPos[i][0], currentGhostPos[i][1]);
|
|
|
|
private int pacmanMoveDist (int[] pacman, int[] xy)
|
|
|
|
{
|
|
|
|
int move;
|
|
|
|
int steps, qStepItems; // distance and group counters
|
|
|
|
boolean done = false; // algo ending flag
|
|
|
|
int r = PacmanUtilities.numberOfRows; // helper for shorting names
|
|
|
|
int c = PacmanUtilities.numberOfColumns; // helper for shorting names
|
|
|
|
int[] xyItem = new int [2]; // Coordinates of the current position of the algo
|
|
|
|
int[] xyNext = new int [2]; // Coordinates of the next valid position of the algo
|
|
|
|
Queue2D q = new Queue2D (r+c - 1); // Queue to feed with possible position
|
|
|
|
int [][] dist = new int [r][c];
|
|
|
|
/*
|
|
|
|
* 2D array holding all the distances from pacman to each square of the maze
|
|
|
|
*/
|
|
|
|
|
|
|
|
// If target square is inside a box abort with max distance
|
|
|
|
if (isInsideBox (xy))
|
|
|
|
return Globals.MAX_DISTANCE;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* init data for algorithm
|
|
|
|
*/
|
|
|
|
for (int i=0 ; i<r ; ++i) {
|
|
|
|
for (int j=0 ; j<c ; ++j) {
|
|
|
|
dist[i][j] = -1;
|
|
|
|
// dist array starts with -1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Creates the @ref ghostNextVectors
|
|
|
|
* these vectors are free vectors from next Pacman's position
|
|
|
|
* to ghosts. This next position is the one is been evaluated.
|
|
|
|
* @return the array holding the vectors
|
|
|
|
*/
|
|
|
|
private Vector[] makeghostNextVectors () {
|
|
|
|
Vector [] ret = new Vector[PacmanUtilities.numberOfGhosts];
|
|
|
|
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
|
|
|
|
ret[i] = new Vector (newX, newY, currentGhostPos[i][0], currentGhostPos[i][1]);
|
|
|
|
// loop data
|
|
|
|
dist [pacman[0]][pacman[1]] = 0; //starting point is the pacman position
|
|
|
|
q.insert (pacman); // feed first loop data
|
|
|
|
qStepItems = 1; // init counters
|
|
|
|
steps = 1;
|
|
|
|
/*
|
|
|
|
* Main loop of the algorithm
|
|
|
|
*
|
|
|
|
* For every position we check the valid moves, we apply to them
|
|
|
|
* the step number and we push them to queue. We group the items in
|
|
|
|
* queue with their step number by measuring how many we push them for
|
|
|
|
* the current steps variable value.
|
|
|
|
*/
|
|
|
|
while (done == false) {
|
|
|
|
if (qStepItems>0) { // Have we any item on the current group?
|
|
|
|
xyItem = q.remove (); //load an item of the current group
|
|
|
|
--qStepItems; // mark the removal of the item
|
|
|
|
for (move=0 ; move<4 ; ++move) {
|
|
|
|
// loop every valid next position for the current position
|
|
|
|
if ((xyNext = pacmanValidMove (xyItem, move)) != Globals.FALSE_POS) {
|
|
|
|
if (dist[xyNext[0]][xyNext[1]] == -1) {
|
|
|
|
// If we haven't been there
|
|
|
|
dist[xyNext[0]][xyNext[1]] = steps; // mark the distance
|
|
|
|
if ((xyNext[0] == xy[0]) && (xyNext[1] == xy[1])) {
|
|
|
|
/*
|
|
|
|
* first match:
|
|
|
|
* The first time we reach destination we have count the
|
|
|
|
* distance. No need any other try
|
|
|
|
*/
|
|
|
|
done = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// If we are not there yet, feed queue with another position
|
|
|
|
q.insert (xyNext);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// We are done with group, mark how many are for the next one
|
|
|
|
if ((qStepItems = q.size()) <= 0)
|
|
|
|
return dist[xy[0]][xy[1]]; // fail safe return
|
|
|
|
++steps; // Update distance counter
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
return dist[xy[0]][xy[1]];
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Evaluate the current move
|
|
|
|
*
|
|
|
|
* The evaluation is made is two faces.
|
|
|
|
* 1) We measure distances between Pacman's next position and
|
|
|
|
* ghosts and we extract a value we call life
|
|
|
|
* 2) We consider the dot products of flag and ghost vector compinations in
|
|
|
|
* order to find which flag has the clearest path to it. This path represent
|
|
|
|
* our goal direction. So we measure the direction from pacman's next move to this
|
|
|
|
* flag and from this distance we extract a value we call goal
|
|
|
|
*/
|
|
|
|
private double evaluate ()
|
|
|
|
{
|
|
|
|
double l=0, g=0; // life and goal helpers
|
|
|
|
int bestFlag = 0; // best flag index in flagPos array
|
|
|
|
double [] M = new double[PacmanUtilities.numberOfFlags];
|
|
|
|
//< Array holding the max normalized dot product for each of the flags
|
|
|
|
Vector candidate; // the distance from Pacman's net position to goal flag
|
|
|
|
|
|
|
|
// Create the vectors
|
|
|
|
flagCurVectors = makeFlagCurVectors ();
|
|
|
|
ghostCurVectors = makeGhostCurVectors ();
|
|
|
|
ghostNextVectors = makeghostNextVectors ();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* life part
|
|
|
|
*/
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
|
|
|
|
ghostNorms[i] = ghostNextVectors[i].norm ();
|
|
|
|
// fill the norm array
|
|
|
|
double ghostEval=0, gd=0;
|
|
|
|
double flagEval=0;
|
|
|
|
int i;
|
|
|
|
int[] flagDist = new int[PacmanUtilities.numberOfFlags];
|
|
|
|
int[] ghostDist = new int[PacmanUtilities.numberOfGhosts];
|
|
|
|
int minGhostDist=Globals.MAX_DISTANCE+1;
|
|
|
|
int averGhostDist; // cast to integer
|
|
|
|
int minFlagDist=Globals.MAX_DISTANCE+1;
|
|
|
|
|
|
|
|
// Find ghost distances, min and average
|
|
|
|
for (i=0, averGhostDist=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
|
|
|
|
ghostDist [i] = ghostMoveDist (currentGhostPos[i], newXY);
|
|
|
|
averGhostDist += ghostDist [i];
|
|
|
|
if (minGhostDist > ghostDist[i])
|
|
|
|
minGhostDist = ghostDist[i];
|
|
|
|
}
|
|
|
|
// extract the life as a combination of the minimum distance and the average
|
|
|
|
l = Globals.EVAL_LIFE_MIN_FACTOR * min (ghostNorms, ghostNorms.length)
|
|
|
|
+ Globals.EVAL_LIFE_AVER_FACTOR * aver (ghostNorms, ghostNorms.length);
|
|
|
|
// normalize the life to our interval
|
|
|
|
life = (-1.0 / (1+l)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) + (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* goal part
|
|
|
|
*/
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
|
|
|
|
for (int j=0 ; j<PacmanUtilities.numberOfGhosts ; ++j) {
|
|
|
|
flagDots[i][j] = Vector.dot (flagCurVectors[i], ghostCurVectors[j]) / ghostCurVectors[j].norm();
|
|
|
|
// fill the dot product array
|
|
|
|
averGhostDist /= PacmanUtilities.numberOfGhosts;
|
|
|
|
// extract the ghostEval as a combination of the minimum distance and the average
|
|
|
|
gd = Globals.EVAL_GHOSTDIST_MIN_FACTOR * minGhostDist + Globals.EVAL_GHOSTDIST_MIN_FACTOR * averGhostDist;
|
|
|
|
// normalize the ghostEval to our interval
|
|
|
|
ghostEval = (-1.0 / (1+gd)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) + (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
|
|
|
|
|
|
|
|
// Find flag distances and min
|
|
|
|
for (i=0 ; i<PacmanUtilities.numberOfFlags ; ++i) {
|
|
|
|
if (currentFlagStatus[i] == false) {
|
|
|
|
flagDist[i] = pacmanMoveDist (newXY, flagPos[i]);
|
|
|
|
if (minFlagDist > flagDist[i])
|
|
|
|
minFlagDist = flagDist[i];
|
|
|
|
}
|
|
|
|
M[i] = max (flagDots[i], flagDots[i].length); // take the max dot product for each flag
|
|
|
|
}
|
|
|
|
bestFlag = bestFlagPosition (M, M.length); // select the best flag
|
|
|
|
// create a vector from pacman's next position to best flag
|
|
|
|
candidate = new Vector (newX, newY, flagPos [bestFlag][0], flagPos [bestFlag][1]);
|
|
|
|
// normalize the flagEval to our interval
|
|
|
|
flagEval = (1.0 / (1+minFlagDist)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) - (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
|
|
|
|
|
|
|
|
g = candidate.norm(); // take the candidate length
|
|
|
|
// normalize the length to our interval
|
|
|
|
goal = (1.0 / (1+g)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) - (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
|
|
|
|
|
|
|
|
// mix the life and goal to output
|
|
|
|
return life * Globals.EVAL_LIFE_FACTOR
|
|
|
|
+ goal * Globals.EVAL_GOAL_FACTOR;
|
|
|
|
return ghostEval * Globals.EVAL_GHOSTDIST_FACTOR
|
|
|
|
+ flagEval * Globals.EVAL_FLAGDIST_FACTOR;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|