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.

266 lines
9.9 KiB

  1. /**
  2. * @file Player.java
  3. *
  4. * @author
  5. * Anastasia Foti AEM:8959
  6. * <anastaskf@ece.auth.gr>
  7. *
  8. * @author
  9. * Christos Choutouridis AEM:8997
  10. * <cchoutou@ece.auth.gr>
  11. */
  12. package host.labyrinth;
  13. /**
  14. * @brief
  15. * This class represents the game's player who cheats.
  16. */
  17. class HeuristicPlayer extends Player {
  18. /** @name Constructors */
  19. /** @{ */
  20. /**
  21. * Create a new player and put him at the row-column coordinates
  22. * @param name The name of the player
  23. * @param champion Flag to indicate if a player is a `champion`
  24. * @param board Reference to the board of the game
  25. * @param row The row coordinate of initial player position
  26. * @param column The column coordinate of initial player's position
  27. */
  28. public HeuristicPlayer(String name, boolean champion, Board board, int row, int column) {
  29. super(name, champion, board, row, column);
  30. }
  31. /**
  32. * Create a new player and put him at the row-column coordinates
  33. * @param name The name of the player
  34. * @param champion Flag to indicate if a player is a `champion`
  35. * @param board Reference to the board of the game
  36. * @param tileId The tileId coordinate of player's initial position
  37. */
  38. public HeuristicPlayer(String name, boolean champion, Board board, int tileId) {
  39. super(name, champion, board, tileId);
  40. }
  41. /** @} */
  42. /** @name Board's main application interface */
  43. /** @{ */
  44. /**
  45. * Utility to get the distance of a possible supply in some direction
  46. * @param currentPos The current position of the player
  47. * @param direction The direction to check
  48. * @return The distance or Const.noSupply
  49. */
  50. int supplyInDirection(int currentPos, int direction) {
  51. Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos));
  52. for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) {
  53. pos = new Position(Position.toRow(pos.getId()), Position.toCol(pos.getId()), direction);
  54. if (board.hasSupply(pos.getId()))
  55. return i+1;
  56. }
  57. return Const.noSupply;
  58. }
  59. /**
  60. * Utility to get the distance of a possible opponent in some direction
  61. * @param currentPos The current position of the player
  62. * @param direction The direction to check
  63. * @return The distance or Const.noOpponent
  64. */
  65. int opponetInDirection(int currentPos, int direction) {
  66. Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos));
  67. int [][] opps = board.getOpponentMoves(playerId);
  68. for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) {
  69. pos = new Position(Position.toRow(pos.getId()), Position.toCol(pos.getId()), direction);
  70. for (int o =0 ; o<opps.length; ++o) {
  71. if (opps[o][0] == pos.getId())
  72. return i+1;
  73. }
  74. }
  75. return Const.noOpponent;
  76. }
  77. /**
  78. * This is the main move evaluation function.
  79. * @param currentPos The current position of the player (before the move to evaluate)
  80. * @param direction The direction (a.k.a. the move) to evaluate
  81. * @return A signed real number. The higher the output, the higher the evaluation.
  82. */
  83. double evaluate (int currentPos, int direction) {
  84. int opDist = opponetInDirection (currentPos, direction);
  85. int supDist = supplyInDirection(currentPos, direction);
  86. // saturate
  87. opDist = (opDist == Const.noOpponent) ? 0: opDist;
  88. supDist = (supDist == Const.noSupply) ? 0 : supDist;
  89. return ((supDist != 0)? (1.0/supDist * Const.supplyFactor) : 0)
  90. - ((opDist != 0) ? (1.0/opDist * Const.opponentFactor) : 0);
  91. }
  92. // Must return a new move always
  93. int getNextMove(int currentPos) {
  94. Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
  95. int N = dirs.size();
  96. double[] eval = new double[N];
  97. int [] eval_dir = new int[N];
  98. for (int i =0, dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get(), ++i) {
  99. if (board.isWalkable(currentPos, dir))
  100. eval[i] = evaluate(currentPos, dir);
  101. else
  102. eval[i] = Double.NEGATIVE_INFINITY;
  103. eval_dir[i] = dir;
  104. }
  105. int dir;
  106. if (isUnevaluated(eval, N)) {
  107. ShuffledRange r = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step);
  108. do
  109. dir = r.get();
  110. while (!board.isWalkable(currentPos, dir));
  111. }
  112. else {
  113. dir = directionOfMax (eval, eval_dir, N);
  114. }
  115. Position new_pos = new Position( Position.toRow(currentPos), Position.toCol(currentPos), dir );
  116. return new_pos.getId();
  117. }
  118. /**
  119. * HeuristicPlayer's move.
  120. *
  121. * A player of this kind cheats. He does not throw a dice to get a direction. In contrary he
  122. * calculates his next move very carefully.
  123. * If the player is a champion then he also picks up a possible supply from the tile.
  124. *
  125. * @param id The id of the starting tile.
  126. * @return An array containing player's final position and possible supply of that position.
  127. * The array format is:
  128. * <ul>
  129. * <li> int[0]: The tileId of the final player's position.
  130. * <li> int[1]: The row of the final player's position.
  131. * <li> int[2]: The column of the final player's position.
  132. * <li> int[3]: The supplyId in case player picked one (Const.noSupply otherwise).
  133. * </ul>
  134. */
  135. @Override
  136. int[] move(int id) {
  137. // Initialize return array with the current data
  138. int[] ret = new int[Const.moveItems];
  139. ret[0] = getNextMove(id);
  140. ret[1] = y = Position.toRow(ret[0]);
  141. ret[2] = x = Position.toCol(ret[0]);
  142. int supplyFlag =0, moveFlag =1;
  143. // In case of a champion player, try also to pick a supply
  144. if (champion && (ret[3] = board.tryPickSupply(ret[0])) != Const.noSupply) {
  145. ++score; // keep score
  146. ++supplyFlag;
  147. }
  148. int dir = Direction.get(id, ret[0]); // update direction counters
  149. ++dirCounter[dir];
  150. board.updateMove(ret, playerId);
  151. // Update supply and opponent distance
  152. int smin =DirRange.End, omin =DirRange.End;
  153. for (int d = DirRange.Begin ; d<DirRange.End ; d += DirRange.Step) {
  154. int s = supplyInDirection (ret[0], d);
  155. int o = opponetInDirection(ret[0], d);
  156. if (s >= 0 && s < smin) smin = s;
  157. if (o >= 0 && o < omin) omin = o;
  158. }
  159. // update path
  160. Integer[] p = {
  161. ret[0], dir, moveFlag, supplyFlag,
  162. dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT],
  163. (smin != DirRange.End)? smin:Const.noSupply, (omin != DirRange.End)? omin:Const.noOpponent
  164. };
  165. path.add(p);
  166. return ret;
  167. }
  168. /**
  169. * Prints round information for the player
  170. */
  171. void statistics() {
  172. if (!path.isEmpty()) {
  173. Integer[] last = path.get(path.size()-1);
  174. String who = String.format("%12s", name);
  175. System.out.print(who + ": score[" + score + "]" + ", dice =" + last[1] + ", tileId =" + last[0] + " (" + Position.toRow(last[0]) + ", " + Position.toCol(last[0]) + ")");
  176. if (last[2] == 0)
  177. System.out.println(" *Can not move.");
  178. else if (last[3] != 0)
  179. System.out.println(" *Found a supply.");
  180. else
  181. System.out.println("");
  182. // extra prints for heuristic
  183. if (last[8] != Const.noSupply) System.out.println(" supply distance =" + last[8]);
  184. else System.out.println(" supply distance = blind");
  185. if (last[9] != Const.noOpponent) System.out.println(" opponent distance =" + last[9]);
  186. else System.out.println(" opponent distance = blind");
  187. }
  188. }
  189. /**
  190. * Prints final statistics for the player
  191. */
  192. void final_statistics () {
  193. String who = String.format("%12s", name);
  194. System.out.println();
  195. System.out.println(who + ": score[" + score + "]");
  196. System.out.println(" Moves up: " + dirCounter[Direction.UP]);
  197. System.out.println(" Moves right: " + dirCounter[Direction.RIGHT]);
  198. System.out.println(" Moves down: " + dirCounter[Direction.DOWN]);
  199. System.out.println(" Moves left: " + dirCounter[Direction.LEFT]);
  200. }
  201. /** @} */
  202. /**
  203. * A small utility to extract the direction of maximum evaluation result.
  204. *
  205. * We search into the \c eval results and find the index of the maximum evaluation.
  206. * Then we return the direction of \c eval_dir matrix at the same index we found.
  207. *
  208. * @param eval Array with evaluation results for each direction
  209. * @param eval_dir Array with the matching direction to \c eval array
  210. * @param N The size of both arrays
  211. * @return The direction of maximum evaluation. If \c eval is empty returns the first item \c eval[0]
  212. * @note
  213. * This function should not be called if there is at least one evaluation result in \c eval
  214. */
  215. private int directionOfMax (double[] eval, int[] eval_dir, int N) {
  216. double M = Double.NEGATIVE_INFINITY;
  217. int M_idx = 0;
  218. for (int i =0; i < N ; ++i) {
  219. if (eval[i] > M) {
  220. M = eval[i];
  221. M_idx = i;
  222. }
  223. }
  224. return eval_dir[M_idx];
  225. }
  226. /**
  227. * A small utility to check if there is at least one evaluation result in the \c eval array
  228. * @param eval The array to check
  229. * @param N The size of the array
  230. * @return True if there is none, false otherwise
  231. */
  232. private boolean isUnevaluated (double[] eval, int N) {
  233. for (int i =0 ; i<N ; ++i)
  234. if (eval[i] != 0 && eval[i] != Double.NEGATIVE_INFINITY)
  235. return false;
  236. return true;
  237. }
  238. /** @name Class data */
  239. /** @{ */
  240. /** @} */
  241. }