diff --git a/GameLog.txt b/GameLog.txt index 0083b23..9eb9e46 100755 --- a/GameLog.txt +++ b/GameLog.txt @@ -4,3 +4,30 @@ Team 0.00 Mine 0 Team 0.00 Mine 1 25 Team 0.00 Mine 0 Team 0.00 Mine 1 25 Team 0.00 Mine 0 Team 0.00 Mine 1 11 Team 0.00 Mine 1 Team 0.00 Mine 0 131 +Team 0.00 Mine 0 Team 0.00 Mine 1 25 +Team 0.00 Mine 0 Team 0.00 Mine 1 44 +Team 0.00 Mine 0 Team 0.00 Mine 1 44 +Team 0.00 Mine 0 Team 0.00 Mine 1 36 +Team 0.00 Mine 0 Team 0.00 Mine 1 36 +Team 0.00 Mine 0 Team 0.00 Mine 1 71 +Team 0.00 Mine 0 Team 0.00 Mine 1 71 +Team 0.00 Mine 0 Team 0.00 Mine 1 165 +Team 0.00 Mine 0 Team 0.00 Mine 1 165 +Team 0.00 Mine 1 Team 0.00 Mine 0 105 +Team 0.00 Mine 1 Team 0.00 Mine 0 88 +Team 0.00 Mine 1 Team 0.00 Mine 0 88 +Team 0.00 Mine 1 Team 0.00 Mine 0 73 +Team 0.00 Mine 1 Team 0.00 Mine 0 73 +Team 0.00 Mine 1 Team 0.00 Mine 0 59 +Team 0.00 Mine 1 Team 0.00 Mine 0 59 +Team 0.00 Mine 1 Team 0.00 Mine 0 140 +Team 0.00 Mine 1 Team 0.00 Mine 0 140 +Team 0.00 Mine 1 Team 0.00 Mine 0 140 +Team 0.00 Mine 0 Team 0.00 Mine 1 62 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 diff --git a/bin/gr/auth/ee/dsproject/pacman/Creature.class b/bin/gr/auth/ee/dsproject/pacman/Creature.class index 41afca3..a461bdb 100755 Binary files a/bin/gr/auth/ee/dsproject/pacman/Creature.class and b/bin/gr/auth/ee/dsproject/pacman/Creature.class differ diff --git a/src/gr/auth/ee/dsproject/node/Globals.java b/src/gr/auth/ee/dsproject/node/Globals.java index 9efd544..a6afd10 100755 --- a/src/gr/auth/ee/dsproject/node/Globals.java +++ b/src/gr/auth/ee/dsproject/node/Globals.java @@ -33,25 +33,37 @@ public class Globals { * Evaluation settings */ /** - * Mixing factor for the minimum distance for life algorithm + * Mixing factor for the minimum distance */ - public static final double EVAL_LIFE_MIN_FACTOR = 0.8; - /** - * mixing factor for the average distances for live algorithm - * @note the factor is the complementary of the EVAL_LIFE_MIN_FACTOR - */ - public static final double EVAL_LIFE_AVER_FACTOR = 1 - EVAL_LIFE_MIN_FACTOR; + public static final double EVAL_GHOSTDIST_MIN_FACTOR = 0.8; + public static final double EVAL_LGHOSTDIST_AVER_FACTOR = 1 - EVAL_GHOSTDIST_MIN_FACTOR; /** - * Evaluation mixing factor representing how mutch of life will be - * in the final evaluation value + * Evaluation mixing factor representing how much the ghost distances will + * affect the final evaluation value */ - public static final double EVAL_LIFE_FACTOR = 0.35; + public static final double EVAL_GHOSTDIST_FACTOR = 0.6; /** - * Evaluation mixing factor representing how mutch of goal will be - * in the final evaluation value - * @note the factor is the complementary of the EVAL_LIFE_FACTOR + * Evaluation mixing factor representing how much the flag distances will + * affect the final evaluation value + */ + public static final double EVAL_FLAGDIST_FACTOR = 1 - EVAL_GHOSTDIST_FACTOR; + + + /* + * In order to find out when a ghost is inside a cavity we manualy + * define the box limits of the cavity boxes of the current maze here + * :) */ - public static final double EVAL_GOAL_FACTOR = 1 - EVAL_LIFE_FACTOR; + public static final int BOXES[][][] = { + { { 5, 5}, { 8, 8} }, + { { 5, 16}, { 8, 19} }, + { {11, 5}, {14, 8} }, + { {11, 16}, {14, 19} } + }; + + public static final int[] FALSE_POS = {-1, -1}; + + public static final int MAX_DISTANCE = 100; } diff --git a/src/gr/auth/ee/dsproject/node/Node89978445.java b/src/gr/auth/ee/dsproject/node/Node89978445.java index 7ab4f26..9821ed7 100755 --- a/src/gr/auth/ee/dsproject/node/Node89978445.java +++ b/src/gr/auth/ee/dsproject/node/Node89978445.java @@ -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=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 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= 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 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 ; i0) { // 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 ; i0) { // 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 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 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; + } } diff --git a/src/gr/auth/ee/dsproject/node/Queue2D.java b/src/gr/auth/ee/dsproject/node/Queue2D.java new file mode 100755 index 0000000..f7c2866 --- /dev/null +++ b/src/gr/auth/ee/dsproject/node/Queue2D.java @@ -0,0 +1,73 @@ +package gr.auth.ee.dsproject.node; + +/** + * @brief + * A queue implementation for (x, y) coordinates + */ +public class Queue2D +{ + int [][] q; + //int f; + int r; + int size, nItem; + + public Queue2D () { + //f = 0; + r = 0; + size = nItem = 0; + q = null; + } + + public Queue2D (int size) { + //f = 0; + r = -1; + nItem = 0; + this.size = size; + q = new int[size][2]; + } + + public int size () { return nItem; } + public boolean isEmpty () { return (nItem == 0) ? true : false; } + public boolean isFull () { return (nItem >= size) ? true : false; } + + public int [] peek () { + int [] none = {-1, -1}; + if (!isEmpty ()) { + return q[0]; //return q[f]; + } + else + return none; + } + + public int [] insert (int [] it) { + int [] none = {-1, -1}; + + if (!isFull ()) { + ++r; + q[r][0] = it[0]; + q[r][1] = it[1]; + ++nItem; + return it; + } + else + return none; + } + + public int [] remove () { + int [] ret = {-1, -1}; + if (!isEmpty ()) { + ret[0] = q[0][0]; //ret[0] = q[f][0]; + ret[1] = q[0][1]; //ret[1] = q[f][1]; + for (int i=0 ; i @@ -60,7 +59,7 @@ public class Creature implements gr.auth.ee.dsproject.pacman.AbstractCreature // loop the possible moves and fill them to list for (int i=0 ; i<4 ; ++i) { - mv = new Node89978445 (Maze, currPosition[0], currPosition[1], i); + mv = new Node89978445 (Maze, currPosition, i); if (mv.getEvaluation() > Globals.NO_EVAL) moves.add (mv); /*