diff --git a/.classpath b/.classpath old mode 100644 new mode 100755 diff --git a/.project b/.project old mode 100644 new mode 100755 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs old mode 100644 new mode 100755 diff --git a/GameLog.txt b/GameLog.txt old mode 100644 new mode 100755 index d6fd94f..eb13d4c --- a/GameLog.txt +++ b/GameLog.txt @@ -108,3 +108,233 @@ Team 0.00 Mine 1 Team 0.00 Mine 0 80 Team 0.00 Mine 1 Team 0.00 Mine 0 78 Team 0.00 Mine 1 Team 0.00 Mine 0 71 Team 0.00 Mine 1 Team 0.00 Mine 0 71 +Team 0.00 Mine 0 Team 0.00 Mine 1 11 +Team 0.00 Mine 0 Team 0.00 Mine 1 11 +Team 0.00 Mine 1 Team 0.00 Mine 0 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 69 +Team 0.00 Mine 1 Team 0.00 Mine 0 69 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 61 +Team 0.00 Mine 1 Team 0.00 Mine 0 61 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 1 Team 0.00 Mine 0 66 +Team 0.00 Mine 1 Team 0.00 Mine 0 66 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 0 Team 0.00 Mine 1 5 +Team 0.00 Mine 0 Team 0.00 Mine 1 5 +Team 0.00 Mine 0 Team 0.00 Mine 1 5 +Team 0.00 Mine 0 Team 0.00 Mine 1 7 +Team 0.00 Mine 0 Team 0.00 Mine 1 7 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 27 +Team 0.00 Mine 0 Team 0.00 Mine 1 27 +Team 0.00 Mine 0 Team 0.00 Mine 1 28 +Team 0.00 Mine 0 Team 0.00 Mine 1 28 +Team 0.00 Mine 0 Team 0.00 Mine 1 28 +Team 0.00 Mine 0 Team 0.00 Mine 1 11 +Team 0.00 Mine 0 Team 0.00 Mine 1 11 +Team 0.00 Mine 1 Team 0.00 Mine 0 52 +Team 0.00 Mine 1 Team 0.00 Mine 0 52 +Team 0.00 Mine 1 Team 0.00 Mine 0 69 +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 56 +Team 0.00 Mine 1 Team 0.00 Mine 0 56 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 76 +Team 0.00 Mine 1 Team 0.00 Mine 0 76 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 1 Team 0.00 Mine 0 64 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 98 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 28 +Team 0.00 Mine 0 Team 0.00 Mine 1 35 +Team 0.00 Mine 0 Team 0.00 Mine 1 15 +Team 0.00 Mine 0 Team 0.00 Mine 1 15 +Team 0.00 Mine 0 Team 0.00 Mine 1 59 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +Team 0.00 Mine 1 Team 0.00 Mine 0 73 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 76 +Team 0.00 Mine 1 Team 0.00 Mine 0 76 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 1 Team 0.00 Mine 0 122 +Team 0.00 Mine 1 Team 0.00 Mine 0 122 +Team 0.00 Mine 1 Team 0.00 Mine 0 78 +Team 0.00 Mine 1 Team 0.00 Mine 0 96 +Team 0.00 Mine 1 Team 0.00 Mine 0 96 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 0 Team 0.00 Mine 1 20 +Team 0.00 Mine 0 Team 0.00 Mine 1 68 +Team 0.00 Mine 0 Team 0.00 Mine 1 68 +Team 0.00 Mine 0 Team 0.00 Mine 1 26 +Team 0.00 Mine 0 Team 0.00 Mine 1 26 +Team 0.00 Mine 0 Team 0.00 Mine 1 26 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 12 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 0 Team 0.00 Mine 1 31 +Team 0.00 Mine 0 Team 0.00 Mine 1 31 +Team 0.00 Mine 0 Team 0.00 Mine 1 42 +Team 0.00 Mine 0 Team 0.00 Mine 1 42 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 1 Team 0.00 Mine 0 131 +Team 0.00 Mine 1 Team 0.00 Mine 0 131 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 114 +Team 0.00 Mine 0 Team 0.00 Mine 1 16 +Team 0.00 Mine 0 Team 0.00 Mine 1 16 +Team 0.00 Mine 0 Team 0.00 Mine 1 65 +Team 0.00 Mine 0 Team 0.00 Mine 1 71 +Team 0.00 Mine 0 Team 0.00 Mine 1 32 +Team 0.00 Mine 0 Team 0.00 Mine 1 32 +Team 0.00 Mine 0 Team 0.00 Mine 1 16 +Team 0.00 Mine 0 Team 0.00 Mine 1 15 +Team 0.00 Mine 0 Team 0.00 Mine 1 65 +Team 0.00 Mine 0 Team 0.00 Mine 1 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 118 +Team 0.00 Mine 1 Team 0.00 Mine 0 118 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 0 Team 0.00 Mine 1 27 +Team 0.00 Mine 0 Team 0.00 Mine 1 27 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 1 Team 0.00 Mine 0 63 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +Team 0.00 Mine 0 Team 0.00 Mine 1 330 +Team 0.00 Mine 1 Team 0.00 Mine 0 60 +Team 0.00 Mine 1 Team 0.00 Mine 0 60 +Team 0.00 Mine 0 Team 0.00 Mine 1 129 +Team 0.00 Mine 0 Team 0.00 Mine 1 129 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 1 Team 0.00 Mine 0 158 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 1 Team 0.00 Mine 0 78 +Team 0.00 Mine 1 Team 0.00 Mine 0 78 +Team 0.00 Mine 1 Team 0.00 Mine 0 75 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 0 Team 0.00 Mine 1 5 +Team 0.00 Mine 0 Team 0.00 Mine 1 5 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +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 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 0 Team 0.00 Mine 1 19 +Team 0.00 Mine 1 Team 0.00 Mine 0 160 +Team 0.00 Mine 1 Team 0.00 Mine 0 79 +Team 0.00 Mine 1 Team 0.00 Mine 0 79 +Team 0.00 Mine 1 Team 0.00 Mine 0 79 +Team 0.00 Mine 0 Team 0.00 Mine 1 56 +Team 0.00 Mine 0 Team 0.00 Mine 1 56 +Team 0.00 Mine 1 Team 0.00 Mine 0 60 +Team 0.00 Mine 1 Team 0.00 Mine 0 60 +Team 0.00 Mine 1 Team 0.00 Mine 0 52 +Team 0.00 Mine 1 Team 0.00 Mine 0 52 +Team 0.00 Mine 1 Team 0.00 Mine 0 110 +Team 0.00 Mine 1 Team 0.00 Mine 0 110 +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 73 +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 71 +Team 0.00 Mine 1 Team 0.00 Mine 0 71 +Team 0.00 Mine 1 Team 0.00 Mine 0 62 +Team 0.00 Mine 0 Team 0.00 Mine 1 68 +Team 0.00 Mine 0 Team 0.00 Mine 1 68 +Team 0.00 Mine 0 Team 0.00 Mine 1 68 +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 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 98 +Team 0.00 Mine 1 Team 0.00 Mine 0 98 +Team 0.00 Mine 1 Team 0.00 Mine 0 86 +Team 0.00 Mine 1 Team 0.00 Mine 0 86 +Team 0.00 Mine 1 Team 0.00 Mine 0 93 +Team 0.00 Mine 1 Team 0.00 Mine 0 93 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 72 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +Team 0.00 Mine 1 Team 0.00 Mine 0 74 +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 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 57 +Team 0.00 Mine 1 Team 0.00 Mine 0 57 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 0 Team 0.00 Mine 1 17 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 66 +Team 0.00 Mine 1 Team 0.00 Mine 0 66 +Team 0.00 Mine 1 Team 0.00 Mine 0 77 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 0 Team 0.00 Mine 1 6 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 1 Team 0.00 Mine 0 84 +Team 0.00 Mine 1 Team 0.00 Mine 0 67 +Team 0.00 Mine 0 Team 0.00 Mine 1 18 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 58 +Team 0.00 Mine 1 Team 0.00 Mine 0 81 +Team 0.00 Mine 1 Team 0.00 Mine 0 81 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 0 Team 0.00 Mine 1 21 +Team 0.00 Mine 1 Team 0.00 Mine 0 133 +Team 0.00 Mine 1 Team 0.00 Mine 0 133 +Team 0.00 Mine 1 Team 0.00 Mine 0 109 +Team 0.00 Mine 1 Team 0.00 Mine 0 109 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 83 +Team 0.00 Mine 1 Team 0.00 Mine 0 85 +Team 0.00 Mine 1 Team 0.00 Mine 0 85 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 70 +Team 0.00 Mine 1 Team 0.00 Mine 0 71 +Team 0.00 Mine 1 Team 0.00 Mine 0 71 +Team 0.00 Mine 1 Team 0.00 Mine 0 60 +Team 0.00 Mine 1 Team 0.00 Mine 0 89 +Team 0.00 Mine 1 Team 0.00 Mine 0 89 +Team 0.00 Mine 0 Team 0.00 Mine 1 55 +Team 0.00 Mine 0 Team 0.00 Mine 1 55 +Team 0.00 Mine 1 Team 0.00 Mine 0 65 +Team 0.00 Mine 1 Team 0.00 Mine 0 65 diff --git a/bin/gr/auth/ee/dsproject/pacman/Creature.class b/bin/gr/auth/ee/dsproject/pacman/Creature.class index fc58a20..3264e9d 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/ghosts.gif b/ghosts.gif old mode 100644 new mode 100755 diff --git a/lib/pacman.jar b/lib/pacman.jar old mode 100644 new mode 100755 diff --git a/pacman.gif b/pacman.gif old mode 100644 new mode 100755 diff --git a/src/gr/auth/ee/dsproject/node/Globals.java b/src/gr/auth/ee/dsproject/node/Globals.java old mode 100644 new mode 100755 index d998a3e..80bc83f --- a/src/gr/auth/ee/dsproject/node/Globals.java +++ b/src/gr/auth/ee/dsproject/node/Globals.java @@ -19,46 +19,55 @@ import gr.auth.ee.dsproject.pacman.PacmanUtilities; * the Node evaluation algorithm */ public class Globals { - /* - * Global constants - */ - public static final int NO_PLACE = -1; // out of region square - //public static final int NO_MOVE = -1; - public static final int INVALID_MOVE = -1; // invalid move marker - - public static final int EVAL_MAX = 100; // out max evaluation value - public static final int EVAL_MIN = -100; // our minimum evaluation value - public static final int NO_EVAL = EVAL_MIN-1; // mark the invalid move or no evaluation - - + + /* * Evaluation settings */ /** * Mixing factor for the minimum distance */ - public static final double EVAL_GHOSTDIST_MIN_FACTOR = 0.8; - public static final double EVAL_LGHOSTDIST_AVER_FACTOR = 1 - EVAL_GHOSTDIST_MIN_FACTOR; - + // ghosts + public static final double EVAL_GHOSTDIST_MIN_FACTOR = 0.85; + public static final double EVAL_GHOSTDIST_AVER_FACTOR = 1 - EVAL_GHOSTDIST_MIN_FACTOR; + public static final double EVAL_GHOSTMINDIST_ISOLATION = 1; + public static final int EVAL_GHOST_BOOST = 2; + public static final int EVAL_GHOSTBOOST_OFFSET = -25; + public static final int EVAL_GHOST_TERRITORY = 6; + public static final int EVAL_GHOSTTERRITORY_OFFSET = -50; + // flags + public static final int EVAL_FLAG_BOOST = 2; + public static final int EVAL_FLAGBOOST_OFFSET = 25; + /** - * Evaluation mixing factor representing how much the ghost distances will - * affect the final evaluation value - */ - public static final double EVAL_GHOSTDIST_FACTOR = 0.6; - /** - * Evaluation mixing factor representing how much the flag distances will - * affect the final evaluation value + * Evaluation mixing factor representing how much the individual + * evaluation values(host, flag, torus) affect the final evaluation of the position */ - public static final double EVAL_FLAGDIST_FACTOR = 1 - EVAL_GHOSTDIST_FACTOR; - + public static final double EVAL_GHOSTDIST_FACTOR = 0.65; + public static final double EVAL_FLAGDIST_FACTOR = 0.40; + public static final double EVAL_TORUSDIST_FACTOR = 0.04; + + /* * Tree settings */ public static final int MINMAXTREE_MAX_DEPTH = 2; // MUST be multiple of 2 + /* - * In order to find out when a ghost is inside a cavity we manualy + * Maze setting + */ + public static final int[] POSITION_FALSE = {-1, -1}; + + public static final int DISTANCE_MAX = PacmanUtilities.numberOfRows + PacmanUtilities.numberOfColumns - 1; + public static final int DISTANCE_FALSE = -1; + + public static final int FLAGDIST_MAX = 26; // add some for safety (lowers the gain) + public static final int BORDERDIST_MAX = 12; // add some for safety (lowers the gain) + + /* + * In order to find out when a creature is inside a cavity we manually * define the box limits of the cavity boxes of the current maze here * :) */ @@ -68,8 +77,39 @@ public class Globals { { {11, 5}, {14, 8} }, { {11, 16}, {14, 19} } }; + + /** + * Torus borders are the squares in the outer limits of the maze where + * there are no walls. Pacman'a gravity can curve the maze plane to + * a torus through these squares. This function check if a box is + * a torus border box + */ + public static final int TORUS_BORDERS[][] = { + {0, 11}, + {0, 12}, + {0, 13}, + {9, 0}, + {9, 24}, + {10, 0}, + {10, 24}, + {19, 11}, + {19, 12}, + {19, 13}, + }; + + + /* + * General algorithm constants + */ + public static final int NO_PLACE = -1; // out of region square + public static final int INVALID_MOVE = -1; // invalid move marker + + public static final int EVAL_MAX = 100; // out max evaluation value + public static final int EVAL_MIN = 0; // our minimum evaluation value + + public static final int EVAL_FINAL_MAX = 100; + public static final int EVAL_FINAL_MIN = -100; - public static final int[] FALSE_POS = {-1, -1}; + public static final int NO_EVAL = EVAL_FINAL_MIN-1; // mark the invalid on no evaluation - public static final int MAX_DISTANCE = PacmanUtilities.numberOfRows + PacmanUtilities.numberOfColumns - 1; } diff --git a/src/gr/auth/ee/dsproject/node/Node89978445.java b/src/gr/auth/ee/dsproject/node/Node89978445.java old mode 100644 new mode 100755 index 4f56728..897c9e1 --- a/src/gr/auth/ee/dsproject/node/Node89978445.java +++ b/src/gr/auth/ee/dsproject/node/Node89978445.java @@ -12,7 +12,6 @@ package gr.auth.ee.dsproject.node; import java.util.ArrayList; -//import java.awt.image.PackedColorModel; import gr.auth.ee.dsproject.pacman.PacmanUtilities; import gr.auth.ee.dsproject.pacman.Room; @@ -25,9 +24,12 @@ import gr.auth.ee.dsproject.pacman.Room; */ public class Node89978445 { - double nodeEvaluation; /*< - * Pacman's current move evaluation + double nodeEvaluation; /** + * Pacman's current move evaluation as return value of evaluation () * This is used also as the "return status" of the object + * @note + * The variable is initialized to Globals.NO_EVAL and calculated only + * after the getEvaluation () call */ int[] nodeXY; // Pacman's current x,y coordinate int[][] curGhostPos; // Array holding the Ghost (x,y) pairs @@ -40,9 +42,9 @@ public class Node89978445 * Tree navigation references. These variables are handled by * PacTree */ - Node89978445 parent; - ArrayList children; - int depth; + Node89978445 parent; // reference to parent node in search tree + ArrayList children; // references to children nodes in search tree + int depth; // the depth of the node in search tree /* * ============ Constructors ============== @@ -51,16 +53,15 @@ public class Node89978445 * @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 Node89978445 object + * Using this constructor means that the user MUST call setMaze() + * and setPosition() manually after the creation of the Node89978445 object */ public Node89978445 () { - // Fill members - this.Maze = null; - nodeXY = Globals.FALSE_POS; + this.Maze = null; // Fill members + nodeXY = Globals.POSITION_FALSE; nodeEvaluation = Globals.NO_EVAL; - parent = null; + parent = null; // allocate objects curGhostPos = new int [PacmanUtilities.numberOfGhosts][2]; @@ -77,39 +78,33 @@ public class Node89978445 */ public Node89978445 (Room[][] Maze, int[] curXY) { - this.Maze = Maze; // Fill members - nodeXY = curXY; + this.Maze = Maze; // Fill members + nodeXY = curXY; nodeEvaluation = Globals.NO_EVAL; - parent = null; + parent = null; // allocate objects curGhostPos = new int [PacmanUtilities.numberOfGhosts][2]; flagPos = new int [PacmanUtilities.numberOfFlags][2]; curFlagStatus = new boolean[PacmanUtilities.numberOfFlags]; children = new ArrayList (); - - // calculate helper arrays - curGhostPos = findGhosts (); - flagPos = findFlags (); - curFlagStatus = checkFlags (); - - //Evaluate the position - nodeEvaluation = evaluate (); } /* * ============== Setters =============== */ /** - * @brief SetMaze (Room) to Node object - * @param maze The room to set + * @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 + * @brief + * Set pacman's position * @param curXY Pacman's current X, Y position */ public void setPosition (int[] curXY) { @@ -121,9 +116,12 @@ public class Node89978445 */ /** - * @brief If not done runs the evaluation algorithm and returns the result - * @note We assume the current position was a valid position - * @return The evaluation result + * @brief + * If not done calls the evaluation algorithm and returns the result + * @return The evaluation result (also stored inside object) + * @note + * We assume the current position was a valid position. + * This routine is not called upon creation of the class */ public double getEvaluation () { @@ -132,7 +130,7 @@ public class Node89978445 // Safety filters if (Maze == null) return Globals.NO_EVAL; - if (nodeXY == Globals.FALSE_POS) + if (nodeXY == Globals.POSITION_FALSE) return Globals.NO_EVAL; // calculate helper arrays curGhostPos = findGhosts (); @@ -145,12 +143,21 @@ public class Node89978445 return nodeEvaluation; } + /** + * @return Nodes position in maze + */ public int[] getCurrentPacmanPos () { return nodeXY; } + /** + * @return Current ghost position ina maze + */ public int[][] getCurrentGhostPos () { return curGhostPos; } + /** + * @return The depth of the node tin the min-max tree + */ public int getDepth () { return depth; } @@ -158,15 +165,32 @@ public class Node89978445 /* * ============= Helper API ============ */ + /** + * @brief + * Convert back a maze position to Room's direction format + * @param nextPos The intention (x, y) coordinates + * @param curPos The current coordinates + * @return The direction + * @arg Room.NORTH (x decreases) + * @arg Room.SOUTH (x increases) + * @arg Room.WEST (y decreases) + * @arg Room.EAST (y increases) + */ public static int moveConv (int[] nextPos, int[] curPos) { int dx = nextPos[0] - curPos[0]; int dy = nextPos[1] - curPos[1]; - if (dx < 0) return Room.NORTH; - else if (dx > 0) return Room.SOUTH; - else if (dy < 0) return Room.WEST; - else return Room.EAST; + if (dx == -1 || dx == PacmanUtilities.numberOfRows-1) + return Room.NORTH; + else if (dx == 1 || dx == -PacmanUtilities.numberOfRows+1) + return Room.SOUTH; + else if (dy == -1 || dy == PacmanUtilities.numberOfColumns-1) + return Room.WEST; + else if (dy == 1 || dy == -PacmanUtilities.numberOfColumns+1) + return Room.EAST; + else + return Globals.INVALID_MOVE; } /** @@ -184,34 +208,35 @@ public class Node89978445 } return ret; } + /** * @brief - * Static version of move validation - * Check if the requested move for a node is valid + * Torus borders are the squares in the outer limits of the maze where + * there are no walls. Pacman's gravity can curve the maze plane to + * a torus through these squares. This function check if a position is + * a torus border position + * @param creature creature's (x,y) + * @return + * Return true if the creature is in a border position. */ - public static int[] pacmanValidMove (Node89978445 node, int move) + public static boolean isTorusSquare (int[] creature) { - int[] newPos = new int[2]; - - // find hypothetical new position - newPos[0] = node.nodeXY[0]; - newPos[1] = node.nodeXY[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; + for (int i=0 ; i PacmanUtilities.numberOfGhosts) + if (++g >= PacmanUtilities.numberOfGhosts) keepGoing = false; } } @@ -259,7 +284,7 @@ public class Node89978445 private int[][] findFlags () { int [][] ret = new int [PacmanUtilities.numberOfFlags][2]; // Make an object to return - int g = 0; // Flag index + int f = 0; // Flag index boolean keepGoing = true; // Boundary check helper variable // Loop entire Maze (i, j) @@ -267,10 +292,10 @@ public class Node89978445 for (int y=0 ; keepGoing && y PacmanUtilities.numberOfFlags) + if (++f >= PacmanUtilities.numberOfFlags) keepGoing = false; } } @@ -281,7 +306,6 @@ public class Node89978445 /** * @brief * Loop through flags and check their status - * @param none * @return Object with flag status */ private boolean[] checkFlags () @@ -300,7 +324,11 @@ public class Node89978445 /* * ============ private evaluation helper methods ============== */ - + + /** + * Helper enumerator to make a function pointer like trick + * inside @ref moveDist() + */ private enum Creature { GHOST, PACMAN } @@ -308,6 +336,11 @@ public class Node89978445 /** * @brief * Check if the requested ghost move is valid + * @param ghost ghost coordinates to check the move validation + * @param move the move to check + * @return + * @arg The resulting position in maze if the move is valid. + * @arg If it is not valid return Globals.POSITION_FALSE */ private int[] ghostValidMove (int[] ghost, int move) { @@ -324,15 +357,30 @@ public class Node89978445 // 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; + return Globals.POSITION_FALSE; + /* + * else if (Maze[newPos[0]][newPos[1]].isFlag ()) + * return Globals.POSITION_FALSE; + * @note + * We comment out the flag part... + * This incorrect behavior help evaluation routine to stop treat + * flag position as asylum. In other words we remove the discontinuity + * of shortestMoveDist() for ghost function. + */ + else if (!isInsideBox(ghost) && (Maze[ghost[0]][ghost[1]].walls[move] == 0)) + return Globals.POSITION_FALSE; + else + return newPos; } /** * @brief * Check if the requested pacman move is valid + * @param pacman pacman coordinates to check the move validation + * @param move the move to check + * @return + * @arg The resulting position in maze if the move is valid. + * @arg If it is not valid return Globals.POSITION_FALSE */ private int[] pacmanValidMove (int[] pacman, int move) { @@ -347,88 +395,98 @@ public class Node89978445 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] < 0) newPos[0] = PacmanUtilities.numberOfRows-1; if (newPos[0] >= PacmanUtilities.numberOfRows ) newPos[0] = 0; - if (newPos[1] < 0) newPos[1] = PacmanUtilities.numberOfColumns; + if (newPos[1] < 0) newPos[1] = PacmanUtilities.numberOfColumns-1; 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; + if (Maze[pacman[0]][pacman[1]].walls[move] == 0) + return Globals.POSITION_FALSE; + else if (Maze[newPos[0]][newPos[1]].isGhost ()) + return Globals.POSITION_FALSE; + else + return newPos; } /** * @brief * A Breadth-first search to find the shortest path distance - * from an origin point to another point in the maze as if a - * a creature could walk through - * @param origin origin (x, y) - * @param xy The x, y coordinate in Maze - * @param creature The type of creature for whom the path is for + * from an origin point to another point in the maze as if + * a creature could walk through origin to xy + * @param origin origin's x, y (starting point) + * @param xy destination's x, y + * @param creature The type of creature for whom the path is for. + * This helper variable let us decide which validation + * function should call. * @return The number of steps from origin to xy + * @note + * As long as Java have not function pointers. I prefer this + * as a work around over interfaces and lambdas. */ - private int moveDist (int[] origin, int[] xy, Creature creature) + private int shortestMoveDist (int[] origin, int[] xy, Creature creature) { - 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 move; // move direction + int steps, qStepItems; // distance and group counters + boolean done = false; // algo ending flag 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 (Globals.MAX_DISTANCE - 1); // Queue to feed with possible position - int [][] dist = new int [r][c]; - /*< - * 2D array holding all the distances from the origin to each square of the maze - */ + // Queue to feed with possible position + Queue2D q = new Queue2D (Globals.DISTANCE_MAX - 1); + // 2D array holding all the distances from the origin to each square of the maze + int[][] dist = new int [PacmanUtilities.numberOfRows][PacmanUtilities.numberOfColumns]; - // If target square is inside a box abort with max distance + /* + * If target square is inside a box abort with max DISTANCE_FALSE + * This is for speeding thing up, as the algorithm would return + * the same thing anyway. + */ if (isInsideBox(xy)) - return Globals.MAX_DISTANCE; + return Globals.DISTANCE_FALSE; /* - * init data for algorithm + * initialize 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 + // loop every valid next position for the current position for (move=0 ; move<4 ; ++move) { - // loop every valid next position for the current position switch (creature) { case GHOST: xyNext = ghostValidMove (xyItem, move); break; case PACMAN: xyNext = pacmanValidMove (xyItem, move); break; - default: xyNext = Globals.FALSE_POS; + default: xyNext = Globals.POSITION_FALSE; + /* + * Function pointer - like behavior + */ } - if (xyNext != Globals.FALSE_POS) { - if (dist[xyNext[0]][xyNext[1]] == -1) { + if (xyNext != Globals.POSITION_FALSE) { + if (dist[xyNext[0]][xyNext[1]] == Globals.DISTANCE_FALSE) { // 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 + * first match: The first time we reach destination we + * have measured the distance. No need any other try */ done = true; break; @@ -444,8 +502,8 @@ public class Node89978445 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 + done = true; // There is no path to destination, abort + ++steps; // Update distance counter } } return dist[xy[0]][xy[1]]; @@ -453,47 +511,103 @@ public class Node89978445 /** * @brief - * Evaluate the current move + * Normalize a distance to our evaluation interval. + * @param v The value to normalize + * @param max The maximum input limit value + * @param out_min The minimum output limit value + * @param out_max The maximum output limit value + */ + private double normalize (double v, double max, double out_min, double out_max) { + return (v / max) * (out_max - out_min) + out_min; + } + + /** + * @brief + * Evaluate the current move. + * The position evaluation is based on shortestMoveDist() results. + * We measure the move distance of: + * 1) Ghosts to current position, as a ghost path, which represent the minimum steps + * needed by a ghost in order to reach the current position avoiding all obstacles + * 2) Pacman to all the flags, which represent the minimum steps needed by pacman + * in order to reach the flags avoiding all obstacles including the curving + * of the plane in the borders + * 3) Pacman to all the torus border squares, which represent the minimum steps needed + * by pacman in order to reach the torus border squares. @ref Globals.TORUS_BORDERS + * + * We mix these values in order to produce the final evaluation value */ private double evaluate () { - 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 + double eval = 0; // mixed evaluation value + double ghostEval = 0, gd; // ghost evaluation value + double flagEval = 0; // flag evaluation value + double torusEval = 0; // torus evaluation value + int[] ghostDist = new int[PacmanUtilities.numberOfGhosts]; // hold the ghost distances + int[] flagDist = new int[PacmanUtilities.numberOfFlags]; // hold the flag distances + int[] torusDist = new int[Globals.TORUS_BORDERS.length]; // hold the torus square distances + int minGhostDist = Globals.DISTANCE_MAX+1; // hold the minimum ghost distance + int averGhostDist = Globals.DISTANCE_MAX; // hold the average ghost distance + int minFlagDist = Globals.DISTANCE_MAX+1; // hold the minimum flag distance + int minTorusDist = Globals.DISTANCE_MAX+1; // hold the minimum torus square distance + int i; // loop counter + + /* + * === Ghost distances part === + */ for (i=0, averGhostDist=0 ; i ghostDist[i]) - minGhostDist = ghostDist[i]; + if ((ghostDist [i] = shortestMoveDist (curGhostPos[i], nodeXY, Creature.GHOST)) >= 1) { + averGhostDist += ghostDist [i]; + if (minGhostDist > ghostDist[i]) + minGhostDist = ghostDist[i]; + } } 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; + gd = Globals.EVAL_GHOSTDIST_MIN_FACTOR * minGhostDist + Globals.EVAL_GHOSTDIST_AVER_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; + if (minGhostDist <= Globals.EVAL_GHOST_BOOST) + ghostEval = normalize (gd, Globals.DISTANCE_MAX ,Globals.EVAL_MIN+Globals.EVAL_GHOSTBOOST_OFFSET, Globals.EVAL_MAX); + else if (minGhostDist <= Globals.EVAL_GHOST_TERRITORY) + ghostEval = normalize (gd, Globals.DISTANCE_MAX, Globals.EVAL_MIN, Globals.EVAL_MAX - Globals.EVAL_GHOSTTERRITORY_OFFSET); + else + ghostEval = normalize (gd, Globals.DISTANCE_MAX, Globals.EVAL_MIN, Globals.EVAL_MAX); - // Find flag distances and min + /* + * === Flag distances part === + */ for (i=0 ; i flagDist[i]) - minFlagDist = flagDist[i]; + if ((flagDist[i] = shortestMoveDist (nodeXY, flagPos[i], Creature.PACMAN)) >= 0) { + if (minFlagDist > flagDist[i]) + minFlagDist = flagDist[i]; + } } } // normalize the flagEval to our interval - flagEval = (1.0 / (1+minFlagDist)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) - (Globals.EVAL_MAX - Globals.EVAL_MIN)/2; - - // mix the life and goal to output - return ghostEval * Globals.EVAL_GHOSTDIST_FACTOR - + flagEval * Globals.EVAL_FLAGDIST_FACTOR; + if (minFlagDist <= Globals.EVAL_FLAG_BOOST) + flagEval = normalize ((double)Globals.FLAGDIST_MAX - minFlagDist, Globals.FLAGDIST_MAX, Globals.EVAL_MIN, Globals.EVAL_MAX+Globals.EVAL_FLAGBOOST_OFFSET); + else + flagEval = normalize ((double)Globals.FLAGDIST_MAX - minFlagDist, Globals.FLAGDIST_MAX, Globals.EVAL_MIN, Globals.EVAL_MAX); + + /* + * === Torus borders distances part === + */ + for (i=0 ; i= 0) { + if (minTorusDist > torusDist[i]) + minTorusDist = torusDist[i]; + } + } + // normalize the flagEval to our interval + torusEval = normalize (Globals.BORDERDIST_MAX-minTorusDist, Globals.BORDERDIST_MAX, Globals.EVAL_MIN, Globals.EVAL_MAX); + /* + * === Mix up the values and return the normalized value back to caller === + */ + eval = ghostEval * Globals.EVAL_GHOSTDIST_FACTOR + + flagEval * Globals.EVAL_FLAGDIST_FACTOR + + torusEval * Globals.EVAL_TORUSDIST_FACTOR; + return normalize (eval, Globals.EVAL_FINAL_MAX, Globals.EVAL_FINAL_MIN, Globals.EVAL_FINAL_MAX); } } diff --git a/src/gr/auth/ee/dsproject/node/Queue2D.java b/src/gr/auth/ee/dsproject/node/Queue2D.java old mode 100644 new mode 100755 diff --git a/src/gr/auth/ee/dsproject/node/Tree.java b/src/gr/auth/ee/dsproject/node/Tree.java old mode 100644 new mode 100755 index 975b974..00882d3 --- a/src/gr/auth/ee/dsproject/node/Tree.java +++ b/src/gr/auth/ee/dsproject/node/Tree.java @@ -1,7 +1,9 @@ /** * @file MinMaxTree.java * @brief - * File containing the Pacman Min-Max Tree class. + * File containing the Pacman Min-Max Tree class. A collection + * of static routines to build a search tree and execute + * a alpha-beta pruning algorithm to it * * @author Christos Choutouridis 8997 cchoutou@ece.auth.gr * @author Konstantina Tsechelidou 8445 konstsec@ece.auth.gr @@ -30,9 +32,11 @@ public class Tree /* * ========== private helpers ============== */ + /** @return the minimum of x and y */ private static double min (double x, double y) { return (x < y) ? x : y; } + /** return the maximum of x and y */ private static double max (double x, double y) { return (x > y) ? x : y; } @@ -59,38 +63,59 @@ public class Tree return null; } + /** + * @brief + * A recursive implementation of an alpha-beta pruning algorithm. + * Alpha–beta pruning is a search algorithm that seeks to decrease + * the number of nodes that are evaluated by the minimax algorithm in its search tree. + * It stops completely evaluating a move when at least one possibility has + * been found that proves the move to be worse than a previously examined move. + * Such moves need not be evaluated further. + * @param node current node of the tree. In the initial call is the root node + * @param depth the maximum depth to investigate + * @param a alpha (start -infinity) + * @param b beta (start +infinity) + * @param maxingPlayer helper variable to switch between min-max behavior + * @return + * reference to node with the maximum evaluation resulting path. + */ public static Node89978445 minmaxAB (Node89978445 node, int depth, double a, double b, boolean maxingPlayer) { double v, ev; Node89978445 vNode = null; - if ((depth == 0) || (node.children.isEmpty())) + if ((depth == 0) || (node.children.isEmpty())) // Escape return node; if (maxingPlayer) { - v = Globals.EVAL_MIN - 1; + // maximizing player part + v = Integer.MIN_VALUE; // start small for max algorithm to work + // recursively call alpha-beta for every child, reducing depth and toggling between min and max for (int i=0 ; i v) { v = ev; vNode = node.children.get (i); } - a = max (v, a); - if (b <= a) + a = max (v, a); // mark alpha + if (b <= a) // cut-off alpha break; } return vNode; } else { - v = Globals.EVAL_MAX + 1; + // minimizing player part + v = Integer.MAX_VALUE; // start big for min algorithm to work + // recursively call alpha-beta for every child, reducing depth and toggling between min and max for (int i=0 ; i