A java PacMan game application for A.U.TH (data structures class)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

500 lines
16 KiB

  1. /**
  2. * @file Node89978445.java
  3. * @brief
  4. * File containing the Node class witch represents
  5. * the moves of Pacman
  6. *
  7. * @author Christos Choutouridis 8997 cchoutou@ece.auth.gr
  8. * @author Konstantina Tsechelidou 8445 konstsec@ece.auth.gr
  9. */
  10. package gr.auth.ee.dsproject.node;
  11. //import java.awt.image.PackedColorModel;
  12. import gr.auth.ee.dsproject.pacman.PacmanUtilities;
  13. import gr.auth.ee.dsproject.pacman.Room;
  14. import gr.auth.ee.dsproject.node.Vector;
  15. /**
  16. * @class Node
  17. * @brief
  18. * This class holds each move of the current round and it's evaluation
  19. *
  20. */
  21. public class Node89978445
  22. {
  23. double nodeEvaluation; /**<
  24. * Pacman's current move evaluation
  25. * This is used also as the "return status" of the object
  26. */
  27. int nodeMove; // Pacman's current move
  28. int nodeX; // Pacman's current x coordinate
  29. int nodeY; // Pacman's current y coordinate
  30. int newX; // Pacman's new x coordinate
  31. int newY; // Pacman's new y coordinate
  32. int[][] currentGhostPos; // Array holding the Ghost (x,y) pairs
  33. int[][] flagPos; // Array holding the flag (x,y) pairs
  34. boolean[] currentFlagStatus; // Array holding the flag captuder status
  35. Room[][] Maze; // copy of the current Maze
  36. /*
  37. * ======== evaluation data ===========
  38. */
  39. Vector[] flagCurVectors; /**<
  40. * Array holding the free vectors from current Pacman's position
  41. * to Flags
  42. * @note
  43. * in case of captured flag the corresponding vector is [0, 0]
  44. */
  45. Vector[] ghostCurVectors; /**<
  46. * Array holding the free vectors from current Pacman's position
  47. * to ghosts
  48. */
  49. Vector[] ghostNextVectors; /**<
  50. * Array holding the free vectors from next Pacman's position
  51. * to ghosts. This next position is the one is been evaluated.
  52. */
  53. double [] ghostNorms; //*< Helping array holding the norms of the ghostNextVectors vectors
  54. double [][] flagDots; /**<
  55. * 2d array holding the normalized dot products of all the compinations
  56. * in flagCurVectors and ghostCurVectors vectors
  57. * rows represent the flags and columns the ghosts
  58. */
  59. double goal, life; // evaluation factors
  60. /*
  61. * ============ Constructors ==============
  62. */
  63. /**
  64. * @brief
  65. * The simple constructor. Just initialize the data
  66. * @note
  67. * Using this constructor means that the user MUST call setMaze(), setPosition()
  68. * and setMove manually after the creation of the Nodexxxx object
  69. */
  70. public Node89978445 ()
  71. {
  72. // Fill members
  73. nodeX = newX = Globals.NO_PLACE;
  74. nodeY = newY = Globals.NO_PLACE;
  75. nodeMove = Globals.INVALID_MOVE;
  76. nodeEvaluation = Globals.NO_EVAL;
  77. //calculate members
  78. //newX += (nodeMove == Room.SOUTH) ? 1:0;
  79. //newX -= (nodeMove == Room.NORTH) ? 1:0;
  80. //newY += (nodeMove == Room.EAST) ? 1:0;
  81. //newY -= (nodeMove == Room.WEST) ? 1:0;
  82. // allocate objects
  83. currentGhostPos = new int [PacmanUtilities.numberOfGhosts][2];
  84. flagPos = new int [PacmanUtilities.numberOfFlags][2];
  85. currentFlagStatus = new boolean[PacmanUtilities.numberOfFlags];
  86. //evaluation data init
  87. ghostNorms = new double [PacmanUtilities.numberOfGhosts];
  88. flagDots = new double [PacmanUtilities.numberOfFlags][PacmanUtilities.numberOfGhosts];
  89. }
  90. /**
  91. * @brief
  92. * Constructor for the node
  93. * @param Maze The current maze object
  94. * @param curX The current pacman's x position
  95. * @param curY The current pacman's y position
  96. * @param move The move under inspection
  97. */
  98. public Node89978445 (Room[][] Maze, int curX, int curY, int move)
  99. {
  100. this.Maze = Maze; // Fill members
  101. nodeX = newX = curX;
  102. nodeY = newY = curY;
  103. nodeMove = move;
  104. nodeEvaluation = Globals.NO_EVAL;
  105. //calculate members
  106. newX += (nodeMove == Room.SOUTH) ? 1:0;
  107. newX -= (nodeMove == Room.NORTH) ? 1:0;
  108. newY += (nodeMove == Room.EAST) ? 1:0;
  109. newY -= (nodeMove == Room.WEST) ? 1:0;
  110. // allocate objects
  111. currentGhostPos = new int [PacmanUtilities.numberOfGhosts][2];
  112. flagPos = new int [PacmanUtilities.numberOfFlags][2];
  113. currentFlagStatus = new boolean[PacmanUtilities.numberOfFlags];
  114. //evaluation data init
  115. ghostNorms = new double [PacmanUtilities.numberOfGhosts];
  116. flagDots = new double [PacmanUtilities.numberOfFlags][PacmanUtilities.numberOfGhosts];
  117. }
  118. /*
  119. * ============== Setters ===============
  120. */
  121. /**
  122. * @brief SetMaze (Room) to Node object
  123. * @param maze The room to set
  124. */
  125. public void setMaze (Room[][] maze) {
  126. this.Maze = maze;
  127. }
  128. /**
  129. * @brief Set pacman's position
  130. * @param curX Pacman's current X position
  131. * @param curY Pacman's current Y position
  132. */
  133. public void setPosition (int curX, int curY) {
  134. nodeX = curX;
  135. nodeY = curY;
  136. }
  137. /**
  138. * @brief Set the move to Node object
  139. * @param move The move under inspection
  140. */
  141. public void setMove (int move) {
  142. nodeMove = move;
  143. //update members
  144. newX += (nodeMove == Room.SOUTH) ? 1:0;
  145. newX -= (nodeMove == Room.NORTH) ? 1:0;
  146. newY += (nodeMove == Room.EAST) ? 1:0;
  147. newY -= (nodeMove == Room.WEST) ? 1:0;
  148. }
  149. /*
  150. * ============== getters =================
  151. */
  152. /**
  153. * @brief If not done runs the evaluation algorithm and returns the result
  154. * @return The evaluation result
  155. */
  156. public double getEvaluation ()
  157. {
  158. // calculate helper arrays
  159. currentGhostPos = findGhosts ();
  160. flagPos = findFlags ();
  161. currentFlagStatus = checkFlags ();
  162. // validation and evaluate move
  163. if (isValidMove ()) {
  164. // If already evaluated do not re-evaluate the move
  165. if (nodeEvaluation == Globals.NO_EVAL)
  166. nodeEvaluation = evaluate ();
  167. }
  168. else {
  169. nodeEvaluation = Globals.NO_EVAL;
  170. }
  171. return nodeEvaluation;
  172. }
  173. /**
  174. * @return
  175. * The current move of the Nodexxxxxxx
  176. */
  177. public int getMove () {
  178. return nodeMove;
  179. }
  180. /*
  181. * ============= Node's private methods =============
  182. */
  183. /**
  184. * @brief
  185. * Loop entire maze and return an object that holds Ghost positions
  186. * @param none
  187. * @return Object with Ghost position
  188. * The first dimension holds the number of the ghost
  189. * The 2nd dimension holds the (x, y) coordinates of the ghost
  190. */
  191. private int[][] findGhosts ()
  192. {
  193. int [][] ret = new int [PacmanUtilities.numberOfGhosts][2]; // Make an object to return
  194. int g = 0; // Ghost index
  195. boolean keepGoing = true; // Boundary check helper variable
  196. // Loop entire Maze (i, j)
  197. for (int x=0 ; keepGoing && x<PacmanUtilities.numberOfRows ; ++x) {
  198. for (int y=0 ; keepGoing && y<PacmanUtilities.numberOfColumns ; ++y) {
  199. if (Maze[x][y].isGhost ()) {
  200. // In case of a Ghost save its position to return object
  201. ret[g][0] = x;
  202. ret[g][1] = y;
  203. // boundary check
  204. if (++g > PacmanUtilities.numberOfGhosts)
  205. keepGoing = false;
  206. }
  207. }
  208. }
  209. return ret;
  210. }
  211. /**
  212. * @brief
  213. * Loop entire maze and return an object that holds flags positions
  214. * @param none
  215. * @return Object with flag positions
  216. * The first dimension holds the number of the flag
  217. * The 2nd dimension holds the (x, y) coordinates of the flag
  218. */
  219. private int[][] findFlags ()
  220. {
  221. int [][] ret = new int [PacmanUtilities.numberOfFlags][2]; // Make an object to return
  222. int g = 0; // Flag index
  223. boolean keepGoing = true; // Boundary check helper variable
  224. // Loop entire Maze (i, j)
  225. for (int x=0 ; keepGoing && x<PacmanUtilities.numberOfRows ; ++x) {
  226. for (int y=0 ; keepGoing && y<PacmanUtilities.numberOfColumns ; ++y) {
  227. if (Maze[x][y].isFlag()) {
  228. // In case of a Ghost save its position to return object
  229. ret[g][0] = x;
  230. ret[g][1] = y;
  231. // boundary check
  232. if (++g > PacmanUtilities.numberOfFlags)
  233. keepGoing = false;
  234. }
  235. }
  236. }
  237. return ret;
  238. }
  239. /**
  240. * @brief
  241. * Loop through flags and check their status
  242. * @param none
  243. * @return Object with flag status
  244. */
  245. private boolean[] checkFlags ()
  246. {
  247. boolean[] ret = new boolean [PacmanUtilities.numberOfFlags];
  248. int x, y;
  249. for (int i=0 ; i<PacmanUtilities.numberOfFlags ; ++i) {
  250. x = flagPos[i][0]; // Get row,col coordinates
  251. y = flagPos[i][1];
  252. ret[i] = (Maze[x][y].isCapturedFlag()) ? true : false;
  253. }
  254. return ret;
  255. }
  256. /**
  257. * @brief
  258. * Check if the requested move is valid
  259. */
  260. private boolean isValidMove ()
  261. {
  262. boolean status = true; //have faith
  263. // filters for the move
  264. if (Maze[nodeX][nodeY].walls[nodeMove] == 0)
  265. status = false;
  266. // keep filling the filters [if any]
  267. return status;
  268. }
  269. /*
  270. * ============ evaluation helper methods ==============
  271. */
  272. /**
  273. * @brief
  274. * return the average valued of the array
  275. * @param x The array to calculate
  276. * @param size The size of the array
  277. * @return The average value
  278. */
  279. private double aver (double [] x, int size)
  280. {
  281. double acc = 0; // accumulator
  282. for (int i=0 ; i<size ; ++i) {
  283. acc += x[i];
  284. }
  285. return acc/size;
  286. }
  287. /**
  288. * @brief
  289. * return the minimum valued item of the array
  290. * @param x The array to filter
  291. * @param size The size of the array
  292. * @return The minimum value
  293. */
  294. private double min (double [] x, int size)
  295. {
  296. double ret = 0;
  297. for (int i=0 ; i<size ; ++i) {
  298. if (i==0 || ret > x[i])
  299. ret = x[i];
  300. }
  301. return ret;
  302. }
  303. /**
  304. * @brief
  305. * return the maximum valued item of the array
  306. * @param x The array to filter
  307. * @param size The size of the array
  308. * @return The maximum value
  309. */
  310. private double max (double [] x, int size) {
  311. double ret = 0;
  312. for (int i=0 ; i<size ; ++i) {
  313. if (i==0 || ret<x[i])
  314. ret = x[i];
  315. }
  316. return ret;
  317. }
  318. /**
  319. * @brief
  320. * return the best flag index which is the minimum
  321. * valued item of the array
  322. * @param x The array to filter
  323. * @param size The size of the array
  324. * @return The minimum value
  325. */
  326. private int bestFlagPosition (double [] x, int size) {
  327. int pos = 0;
  328. double min = 9999999; // Start large enough
  329. for (int i=0 ; i<size ; ++i) {
  330. if ((min > x[i]) && (x[i] != 0)) {
  331. /*<
  332. * here we exclude the zero vectors from been candidates
  333. * as these vectors represent the captured flags
  334. */
  335. min = x[i];
  336. pos = i;
  337. }
  338. }
  339. return pos;
  340. }
  341. /**
  342. * @brief
  343. * Creates the @ref flagCurVectors
  344. * these vectors are free vectors from current Pacman's position
  345. * to Flags
  346. * @note
  347. * in case of captured flag the corresponding vector is [0, 0]
  348. * @return the array holding the vectors
  349. */
  350. private Vector[] makeFlagCurVectors () {
  351. Vector[] ret = new Vector[PacmanUtilities.numberOfFlags];
  352. for (int i=0 ; i<PacmanUtilities.numberOfFlags ; ++i) {
  353. if (currentFlagStatus[i] == true)
  354. ret[i] = new Vector (); // eliminate the capture vectors
  355. else
  356. ret[i] = new Vector (nodeX, nodeY, flagPos[i][0], flagPos[i][1]);
  357. }
  358. return ret;
  359. }
  360. /**
  361. * @brief
  362. * Creates the @ref ghostCurVectors
  363. * these vectors are free vectors from current Pacman's position
  364. * to ghosts
  365. * @return the array holding the vectors
  366. */
  367. private Vector[] makeGhostCurVectors () {
  368. Vector [] ret = new Vector[PacmanUtilities.numberOfGhosts];
  369. for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
  370. ret[i] = new Vector (nodeX, nodeY, currentGhostPos[i][0], currentGhostPos[i][1]);
  371. }
  372. return ret;
  373. }
  374. /**
  375. * @brief
  376. * Creates the @ref ghostNextVectors
  377. * these vectors are free vectors from next Pacman's position
  378. * to ghosts. This next position is the one is been evaluated.
  379. * @return the array holding the vectors
  380. */
  381. private Vector[] makeghostNextVectors () {
  382. Vector [] ret = new Vector[PacmanUtilities.numberOfGhosts];
  383. for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
  384. ret[i] = new Vector (newX, newY, currentGhostPos[i][0], currentGhostPos[i][1]);
  385. }
  386. return ret;
  387. }
  388. /**
  389. * @brief
  390. * Evaluate the current move
  391. *
  392. * The evaluation is made is two faces.
  393. * 1) We measure distances between Pacman's next position and
  394. * ghosts and we extract a value we call life
  395. * 2) We consider the dot products of flag and ghost vector compinations in
  396. * order to find which flag has the clearest path to it. This path represent
  397. * our goal direction. So we measure the direction from pacman's next move to this
  398. * flag and from this distance we extract a value we call goal
  399. */
  400. private double evaluate ()
  401. {
  402. double l=0, g=0; // life and goal helpers
  403. int bestFlag = 0; // best flag index in flagPos array
  404. double [] M = new double[PacmanUtilities.numberOfFlags];
  405. //< Array holding the max normalized dot product for each of the flags
  406. Vector candidate; // the distance from Pacman's net position to goal flag
  407. // Create the vectors
  408. flagCurVectors = makeFlagCurVectors ();
  409. ghostCurVectors = makeGhostCurVectors ();
  410. ghostNextVectors = makeghostNextVectors ();
  411. /*
  412. * life part
  413. */
  414. for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
  415. ghostNorms[i] = ghostNextVectors[i].norm ();
  416. // fill the norm array
  417. }
  418. // extract the life as a combination of the minimum distance and the average
  419. l = Globals.EVAL_LIFE_MIN_FACTOR * min (ghostNorms, ghostNorms.length)
  420. + Globals.EVAL_LIFE_AVER_FACTOR * aver (ghostNorms, ghostNorms.length);
  421. // normalize the life to our interval
  422. life = (-1.0 / (1+l)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) + (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
  423. /*
  424. * goal part
  425. */
  426. for (int i=0 ; i<PacmanUtilities.numberOfGhosts ; ++i) {
  427. for (int j=0 ; j<PacmanUtilities.numberOfGhosts ; ++j) {
  428. flagDots[i][j] = Vector.dot (flagCurVectors[i], ghostCurVectors[j]) / ghostCurVectors[j].norm();
  429. // fill the dot product array
  430. }
  431. M[i] = max (flagDots[i], flagDots[i].length); // take the max dot product for each flag
  432. }
  433. bestFlag = bestFlagPosition (M, M.length); // select the best flag
  434. // create a vector from pacman's next position to best flag
  435. candidate = new Vector (newX, newY, flagPos [bestFlag][0], flagPos [bestFlag][1]);
  436. g = candidate.norm(); // take the candidate length
  437. // normalize the length to our interval
  438. goal = (1.0 / (1+g)) * (Globals.EVAL_MAX - Globals.EVAL_MIN) - (Globals.EVAL_MAX - Globals.EVAL_MIN)/2;
  439. // mix the life and goal to output
  440. return life * Globals.EVAL_LIFE_FACTOR
  441. + goal * Globals.EVAL_GOAL_FACTOR;
  442. }
  443. }