|
- /**
- * @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;
- }
-
- }
|