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.

HeuristicPlayer.java 10 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. /**
  2. * @file HeuristicPlayer.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) throws Exception {
  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) throws Exception {
  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(pos.getRow(), pos.getCol(), 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 [] opp = board.getOpponentMove(playerId);
  68. for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) {
  69. pos = new Position(pos.getRow(), pos.getCol(), direction);
  70. if (opp[MOVE_TILE_ID] == pos.getId())
  71. return i+1;
  72. }
  73. return Const.noOpponent;
  74. }
  75. /**
  76. * This is the main move evaluation function.
  77. * @param currentPos The current position of the player (before the move to evaluate)
  78. * @param direction The direction (a.k.a. the move) to evaluate
  79. * @return A signed real number. The higher the output, the higher the evaluation.
  80. */
  81. double evaluate (int currentPos, int direction) {
  82. int opDist = opponetInDirection (currentPos, direction);
  83. int supDist = supplyInDirection(currentPos, direction);
  84. // saturate
  85. opDist = (opDist == Const.noOpponent) ? 0: opDist;
  86. supDist = (supDist == Const.noSupply) ? 0 : supDist;
  87. return ((supDist != 0)? (1.0/supDist * Const.supplyFactor) : 0)
  88. - ((opDist != 0) ? (1.0/opDist * Const.opponentFactor) : 0);
  89. }
  90. /**
  91. * Selects the best possible move to return
  92. * @param currentPos Player's current position to the board
  93. * @return The move array
  94. *
  95. * @note
  96. * This function always return a new move.
  97. */
  98. int[] getNextMove(int currentPos) {
  99. Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
  100. int N = dirs.size();
  101. double[] eval = new double[N];
  102. int [] eval_dir = new int[N];
  103. for (int i =0, dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get(), ++i) {
  104. if (board.isWalkable(currentPos, dir))
  105. eval[i] = evaluate(currentPos, dir);
  106. else
  107. eval[i] = Double.NEGATIVE_INFINITY;
  108. eval_dir[i] = dir;
  109. }
  110. int dir;
  111. if (isUnevaluated(eval, N)) {
  112. ShuffledRange r = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step);
  113. do
  114. dir = r.get();
  115. while (!board.isWalkable(currentPos, dir));
  116. }
  117. else {
  118. dir = directionOfMax (eval, eval_dir, N);
  119. }
  120. Position new_pos = new Position( Position.toRow(currentPos), Position.toCol(currentPos), dir );
  121. int [] ret = {new_pos.getId(), new_pos.getRow(), new_pos.getCol(), dir};
  122. return ret;
  123. }
  124. /**
  125. * HeuristicPlayer's move.
  126. *
  127. * A player of this kind cheats. He does not throw a dice to get a direction. In contrary he
  128. * calculates his next move very carefully.
  129. * If the player is a champion then he also picks up a possible supply from the tile.
  130. *
  131. * @param id The id of the starting tile.
  132. * @return An array containing player's final position and possible supply of that position.
  133. * The array format is:
  134. * <ul>
  135. * <li> int[0]: The tileId of the final player's position.
  136. * <li> int[1]: The row of the final player's position.
  137. * <li> int[2]: The column of the final player's position.
  138. * <li> int[3]: The dice/direction of the move.
  139. * </ul>
  140. */
  141. @Override
  142. int[] move(int id) {
  143. // Initialize return array with the current data
  144. int[] ret = getNextMove(id);
  145. y = Position.toRow(ret[MOVE_TILE_ID]);
  146. x = Position.toCol(ret[MOVE_TILE_ID]);
  147. int supplyFlag =0, moveFlag =1;
  148. // In case of a champion player, try also to pick a supply
  149. if (champion && (board.tryPickSupply(ret[MOVE_TILE_ID]) != Const.noSupply)) {
  150. ++score; // keep score
  151. ++supplyFlag;
  152. }
  153. ++dirCounter[ret[MOVE_DICE]]; // update direction counters
  154. board.updateMove(ret, playerId);
  155. // Update supply and opponent distance
  156. int smin =DirRange.End, omin =DirRange.End;
  157. for (int d = DirRange.Begin ; d<DirRange.End ; d += DirRange.Step) {
  158. int s = supplyInDirection (ret[MOVE_TILE_ID], d);
  159. int o = opponetInDirection(ret[MOVE_TILE_ID], d);
  160. if (s >= 0 && s < smin) smin = s;
  161. if (o >= 0 && o < omin) omin = o;
  162. }
  163. // update path
  164. Integer[] p = {
  165. ret[MOVE_TILE_ID], ret[MOVE_DICE], moveFlag, supplyFlag,
  166. dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT],
  167. (smin != DirRange.End)? smin:Const.noSupply, (omin != DirRange.End)? omin:Const.noOpponent
  168. };
  169. path.add(p);
  170. return ret;
  171. }
  172. /**
  173. * Prints round information for the player
  174. */
  175. void statistics() {
  176. if (!path.isEmpty()) {
  177. Integer[] last = path.get(path.size()-1);
  178. String who = String.format("%12s", name);
  179. System.out.print(who + ": score[" + score + "]" + ", dice =" + last[1] + ", tileId =" + last[0] + " (" + Position.toRow(last[0]) + ", " + Position.toCol(last[0]) + ")");
  180. if (last[2] == 0)
  181. System.out.println(" *Can not move.");
  182. else if (last[3] != 0)
  183. System.out.println(" *Found a supply.");
  184. else
  185. System.out.println("");
  186. // extra prints for heuristic
  187. if (last[8] != Const.noSupply) System.out.println(" supply =" + last[8]);
  188. else System.out.println(" supply = blind");
  189. if (last[9] != Const.noOpponent) System.out.println(" opponent =" + last[9]);
  190. else System.out.println(" opponent = blind");
  191. }
  192. }
  193. /**
  194. * Prints final statistics for the player
  195. */
  196. void final_statistics () {
  197. String who = String.format("%12s", name);
  198. System.out.println();
  199. System.out.println(who + ": score[" + score + "]");
  200. System.out.println(" Moves up: " + dirCounter[Direction.UP]);
  201. System.out.println(" Moves right: " + dirCounter[Direction.RIGHT]);
  202. System.out.println(" Moves down: " + dirCounter[Direction.DOWN]);
  203. System.out.println(" Moves left: " + dirCounter[Direction.LEFT]);
  204. }
  205. /** @} */
  206. /**
  207. * A small utility to extract the direction of maximum evaluation result.
  208. *
  209. * We search into the \c eval results and find the index of the maximum evaluation.
  210. * Then we return the direction of \c eval_dir matrix at the same index we found.
  211. *
  212. * @param eval Array with evaluation results for each direction
  213. * @param eval_dir Array with the matching direction to \c eval array
  214. * @param N The size of both arrays
  215. * @return The direction of maximum evaluation. If \c eval is empty returns the first item \c eval[0]
  216. * @note
  217. * This function should not be called if there is at least one evaluation result in \c eval
  218. */
  219. private int directionOfMax (double[] eval, int[] eval_dir, int N) {
  220. double M = Double.NEGATIVE_INFINITY;
  221. int M_idx = 0;
  222. for (int i =0; i < N ; ++i) {
  223. if (eval[i] > M) {
  224. M = eval[i];
  225. M_idx = i;
  226. }
  227. }
  228. return eval_dir[M_idx];
  229. }
  230. /**
  231. * A small utility to check if there is at least one evaluation result in the \c eval array
  232. * @param eval The array to check
  233. * @param N The size of the array
  234. * @return True if there is none, false otherwise
  235. */
  236. private boolean isUnevaluated (double[] eval, int N) {
  237. for (int i =0 ; i<N ; ++i)
  238. if (eval[i] != 0 && eval[i] != Double.NEGATIVE_INFINITY)
  239. return false;
  240. return true;
  241. }
  242. /** @name Class data */
  243. /** @{ */
  244. /** @} */
  245. }