|
|
@@ -0,0 +1,499 @@ |
|
|
|
/**
|
|
|
|
* @file Node89978445.java
|
|
|
|
* @brief
|
|
|
|
* File containing the Node class witch represents
|
|
|
|
* the moves of Pacman
|
|
|
|
*
|
|
|
|
* @author Christos Choutouridis 8997 cchoutou@ece.auth.gr
|
|
|
|
* @author Konstantina Tsechelidou 8445 konstsec@ece.auth.gr
|
|
|
|
*/
|
|
|
|
|
|
|
|
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
|
|
|
|
* @brief
|
|
|
|
* This class holds each move of the current round and it's evaluation
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
public class Node89978445
|
|
|
|
{
|
|
|
|
double nodeEvaluation; /**<
|
|
|
|
* Pacman's current move evaluation
|
|
|
|
* 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[][] 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
|
|
|
|
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 ==============
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* 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
|
|
|
|
*/
|
|
|
|
public Node89978445 ()
|
|
|
|
{
|
|
|
|
// Fill members
|
|
|
|
nodeX = newX = Globals.NO_PLACE;
|
|
|
|
nodeY = newY = Globals.NO_PLACE;
|
|
|
|
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
|
|
|
|
* @param Maze The current maze object
|
|
|
|
* @param curX The current pacman's x position
|
|
|
|
* @param curY The current pacman's y position
|
|
|
|
* @param move The move under inspection
|
|
|
|
*/
|
|
|
|
public Node89978445 (Room[][] Maze, int curX, int curY, int move)
|
|
|
|
{
|
|
|
|
this.Maze = Maze; // Fill members
|
|
|
|
nodeX = newX = curX;
|
|
|
|
nodeY = newY = curY;
|
|
|
|
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;
|
|
|
|
|
|
|
|
// 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];
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============== Setters ===============
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @brief SetMaze (Room) to Node object
|
|
|
|
* @param maze The room to set
|
|
|
|
*/
|
|
|
|
public void setMaze (Room[][] maze) {
|
|
|
|
this.Maze = maze;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set pacman's position
|
|
|
|
* @param curX Pacman's current X position
|
|
|
|
* @param curY Pacman's current Y position
|
|
|
|
*/
|
|
|
|
public void setPosition (int curX, int curY) {
|
|
|
|
nodeX = curX;
|
|
|
|
nodeY = curY;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Set the move to Node object
|
|
|
|
* @param move The move under inspection
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============== getters =================
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief If not done runs the evaluation algorithm and returns the result
|
|
|
|
* @return The evaluation result
|
|
|
|
*/
|
|
|
|
public double getEvaluation ()
|
|
|
|
{
|
|
|
|
// calculate helper arrays
|
|
|
|
currentGhostPos = findGhosts ();
|
|
|
|
flagPos = findFlags ();
|
|
|
|
currentFlagStatus = checkFlags ();
|
|
|
|
|
|
|
|
// validation and evaluate move
|
|
|
|
if (isValidMove ()) {
|
|
|
|
// If already evaluated do not re-evaluate the move
|
|
|
|
if (nodeEvaluation == Globals.NO_EVAL)
|
|
|
|
nodeEvaluation = evaluate ();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nodeEvaluation = Globals.NO_EVAL;
|
|
|
|
}
|
|
|
|
return nodeEvaluation;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @return
|
|
|
|
* The current move of the Nodexxxxxxx
|
|
|
|
*/
|
|
|
|
public int getMove () {
|
|
|
|
return nodeMove;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ============= Node's private methods =============
|
|
|
|
*/
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Loop entire maze and return an object that holds Ghost positions
|
|
|
|
* @param none
|
|
|
|
* @return Object with Ghost position
|
|
|
|
* The first dimension holds the number of the ghost
|
|
|
|
* The 2nd dimension holds the (x, y) coordinates of the ghost
|
|
|
|
*/
|
|
|
|
private int[][] findGhosts ()
|
|
|
|
{
|
|
|
|
int [][] ret = new int [PacmanUtilities.numberOfGhosts][2]; // Make an object to return
|
|
|
|
int g = 0; // Ghost index
|
|
|
|
boolean keepGoing = true; // Boundary check helper variable
|
|
|
|
|
|
|
|
// Loop entire Maze (i, j)
|
|
|
|
for (int x=0 ; keepGoing && x<PacmanUtilities.numberOfRows ; ++x) {
|
|
|
|
for (int y=0 ; keepGoing && y<PacmanUtilities.numberOfColumns ; ++y) {
|
|
|
|
if (Maze[x][y].isGhost ()) {
|
|
|
|
// In case of a Ghost save its position to return object
|
|
|
|
ret[g][0] = x;
|
|
|
|
ret[g][1] = y;
|
|
|
|
// boundary check
|
|
|
|
if (++g > PacmanUtilities.numberOfGhosts)
|
|
|
|
keepGoing = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Loop entire maze and return an object that holds flags positions
|
|
|
|
* @param none
|
|
|
|
* @return Object with flag positions
|
|
|
|
* The first dimension holds the number of the flag
|
|
|
|
* The 2nd dimension holds the (x, y) coordinates of the flag
|
|
|
|
*/
|
|
|
|
private int[][] findFlags ()
|
|
|
|
{
|
|
|
|
int [][] ret = new int [PacmanUtilities.numberOfFlags][2]; // Make an object to return
|
|
|
|
int g = 0; // Flag index
|
|
|
|
boolean keepGoing = true; // Boundary check helper variable
|
|
|
|
|
|
|
|
// Loop entire Maze (i, j)
|
|
|
|
for (int x=0 ; keepGoing && x<PacmanUtilities.numberOfRows ; ++x) {
|
|
|
|
for (int y=0 ; keepGoing && y<PacmanUtilities.numberOfColumns ; ++y) {
|
|
|
|
if (Maze[x][y].isFlag()) {
|
|
|
|
// In case of a Ghost save its position to return object
|
|
|
|
ret[g][0] = x;
|
|
|
|
ret[g][1] = y;
|
|
|
|
// boundary check
|
|
|
|
if (++g > PacmanUtilities.numberOfFlags)
|
|
|
|
keepGoing = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Loop through flags and check their status
|
|
|
|
* @param none
|
|
|
|
* @return Object with flag status
|
|
|
|
*/
|
|
|
|
private boolean[] checkFlags ()
|
|
|
|
{
|
|
|
|
boolean[] ret = new boolean [PacmanUtilities.numberOfFlags];
|
|
|
|
int x, y;
|
|
|
|
|
|
|
|
for (int i=0 ; i<PacmanUtilities.numberOfFlags ; ++i) {
|
|
|
|
x = flagPos[i][0]; // Get row,col coordinates
|
|
|
|
y = flagPos[i][1];
|
|
|
|
ret[i] = (Maze[x][y].isCapturedFlag()) ? true : false;
|
|
|
|
}
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
private double aver (double [] x, int size)
|
|
|
|
{
|
|
|
|
double acc = 0; // accumulator
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
acc += x[i];
|
|
|
|
}
|
|
|
|
return acc/size;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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)
|
|
|
|
{
|
|
|
|
double ret = 0;
|
|
|
|
|
|
|
|
for (int i=0 ; i<size ; ++i) {
|
|
|
|
if (i==0 || ret > x[i])
|
|
|
|
ret = x[i];
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
*/
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief
|
|
|
|
* Creates the @ref ghostCurVectors
|
|
|
|
* these vectors are free vectors from current Pacman's position
|
|
|
|
* to ghosts
|
|
|
|
* @return the array holding the vectors
|
|
|
|
*/
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @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
|
|
|
|
}
|
|
|
|
// 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
|
|
|
|
}
|
|
|
|
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]);
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|