A java snake game for A.U.TH. Data structures class
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

291 lignes
10 KiB

  1. package net.hoo2.auth.dsproject.snake;
  2. import java.util.*;
  3. /**
  4. * @class MinMaxPlayer
  5. * @brief Represent a MinMax Player in the Game
  6. *
  7. * The players are playing in a round-robin sequence and we keep track
  8. * for each one of them their playing order, score and place on the board.
  9. * This kind of player, is a cheater. He can control the dice. Not fair dude.
  10. *
  11. * @author Christos Choutouridis AEM:8997
  12. * @email cchoutou@ece.auth.gr
  13. */
  14. public class MinMaxPlayer
  15. extends Player {
  16. static final int MINIMAX_TREE_DEPTH = 2; /**< The maximum depth of the minimax tree */
  17. /** @name Constructors */
  18. /** @{ */
  19. /** Default doing nothing constructor */
  20. public MinMaxPlayer() {
  21. super ();
  22. path = new ArrayList<Integer[]>();
  23. }
  24. /**
  25. * @brief The main constructor
  26. *
  27. * This creates a player for the game
  28. * @param playerId The player's to create
  29. * @param name The name of the player
  30. * @param board Reference to the board the player will play on.
  31. */
  32. MinMaxPlayer (int playerId, String name) {
  33. super (playerId, name);
  34. path = new ArrayList<Integer[]>();
  35. }
  36. /* @} */
  37. /** @name Get/Set interface */
  38. /** @{ */
  39. ArrayList<Integer[]> getPath() { return path; }
  40. void setPath (ArrayList<Integer[]> path) {
  41. this.path = path;
  42. }
  43. /** @} */
  44. /**
  45. * Override dice functionality for the player
  46. * @return As this is called from the game only to select playing order
  47. * we cheat and return 1
  48. */
  49. @Override
  50. int dice () {
  51. return 1;
  52. }
  53. /**
  54. * Override get the next move after the user's move
  55. * @param board The board in which we play on
  56. * @param tile The initial tile
  57. * @return The the move as an array
  58. * See @ref Node.nodeMove
  59. */
  60. @Override
  61. int[] getNextMove (Board board, int tile) {
  62. int [] ret = new int[4];
  63. Node root = new Node (board);
  64. createMySubtree(root, 1, tile, selectOpponent(board).getTile());
  65. // Evaluate each possible dice result and find the better one
  66. Node r = MaxValue (root, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY);
  67. // Do the move and get the move data
  68. Integer[] move_data = Arrays.stream (move (board, tile, r.getNodeMove()[3], true))
  69. .boxed()
  70. .toArray(Integer[]::new);
  71. // Store the move data
  72. path.add(move_data);
  73. ret[0] = move_data[MOVE_TILE_IDX];
  74. ret[1] = move_data[MOVE_INITTILE_IDX];
  75. ret[2] = move_data[MOVE_POINTS_IDX];
  76. ret[3] = move_data[MOVE_ROLL_IDX];
  77. return ret; // return the new tile position
  78. }
  79. /**
  80. * The MinMax statistics version
  81. * @param verbose Flag to select the verbosity
  82. * @param sum Flag to select if we need to print a summarize of the user history
  83. */
  84. @Override
  85. void statistics (boolean verbose, boolean sum) {
  86. if (sum) {
  87. // If we run the summarize
  88. int nSnakes =0;
  89. int nLadders =0;
  90. int nRedApples =0;
  91. int nBlackApples =0;
  92. // Calculate frequencies
  93. for (int i=0 ; i<path.size() ; ++i) {
  94. nSnakes += path.get(i)[MOVE_SNAKES_IDX];
  95. nLadders+= path.get(i)[MOVE_LADDERS_IDX];
  96. nRedApples += path.get(i)[MOVE_RED_APPLES_IDX];
  97. nBlackApples += path.get(i)[MOVE_BLACK_APPLES_IDX];
  98. }
  99. // Print the results
  100. System.out.println("");
  101. System.out.println("*** Statistics for " + name + " ***");
  102. System.out.println(" Number of Snake bites : " + nSnakes);
  103. System.out.println(" Number of Ladders used : " + nLadders);
  104. System.out.println(" Number of Red Apples eaten : " + nRedApples);
  105. System.out.println(" Number of Black Apples eaten: " + nBlackApples);
  106. }
  107. else
  108. // Call the base version
  109. super.statistics(verbose, sum);
  110. }
  111. /** @name Private helper API */
  112. /** @{ */
  113. /**
  114. * Select the the best Opponent
  115. * @param board Reference to current board
  116. * @return Reference to the player who is most far in the board
  117. */
  118. private Player selectOpponent (Board board) {
  119. Player opp = null;
  120. int max_tile = Integer.MIN_VALUE;
  121. for (Player p : board.getPlayers()) {
  122. if (p == this) // Please do not return me
  123. continue;
  124. if (opp == null) opp = p; // init variable
  125. if (p.getTile() > max_tile) { // max filtering
  126. opp = p;
  127. max_tile = p.getTile();
  128. }
  129. }
  130. return opp;
  131. }
  132. /**
  133. * One of the 2 recursive functions for creating the minimax tree. This one
  134. * creates children for the MinMax player
  135. * @param parent The parent Node
  136. * @param depth the current depth for the children
  137. * @param tile the tile of MinMax player
  138. * @param oppTile the tile of the best opponent
  139. */
  140. private void createMySubtree (Node parent, int depth, int tile, int oppTile) {
  141. int [] moveData;
  142. int [] nodeMove;
  143. for (int roll = 1 ; roll <= 6 ; ++roll) {
  144. Board nodeBoard = new Board (parent.getNodeBoard()); // clone board
  145. moveData = move (nodeBoard, tile, roll, false); // simulate move
  146. nodeMove = new int[4]; // create nodeMove
  147. nodeMove[0] = moveData[MOVE_TILE_IDX];
  148. nodeMove[1] = moveData[MOVE_INITTILE_IDX];
  149. nodeMove[2] = moveData[MOVE_POINTS_IDX];
  150. nodeMove[3] = moveData[MOVE_ROLL_IDX];
  151. // make child Node
  152. Node child = new Node (parent, depth, nodeMove, nodeBoard);
  153. parent.addChild(child); // add child to tree
  154. createOppSubtree (child, depth+1, nodeMove[0], oppTile);
  155. }
  156. }
  157. /**
  158. * One of the 2 recursive functions for creating the minimax tree. This one
  159. * creates children for the opponent player
  160. * @param parent The parent Node
  161. * @param depth the current depth for the children
  162. * @param tile the tile of MinMax player
  163. * @param oppTile the tile of the best opponent
  164. */
  165. private void createOppSubtree (Node parent, int depth, int tile, int oppTile) {
  166. int [] moveData;
  167. int [] nodeMove;
  168. for (int roll =1 ; roll <= 6 ; ++roll) {
  169. Board nodeBoard = new Board(parent.getNodeBoard()); // clone board
  170. moveData = move (nodeBoard, oppTile, roll, false); // simulate move
  171. nodeMove = new int[4];
  172. nodeMove[0] = moveData[MOVE_TILE_IDX];
  173. nodeMove[1] = moveData[MOVE_INITTILE_IDX];
  174. nodeMove[2] = moveData[MOVE_POINTS_IDX];
  175. nodeMove[3] = moveData[MOVE_ROLL_IDX];
  176. Node child = new Node (parent, depth, nodeMove, nodeBoard); // make child Node
  177. parent.addChild(child); // add child to tree
  178. if (depth >= MINIMAX_TREE_DEPTH) {
  179. child.setNodeEvaluation(
  180. evaluate(parent.getNodeMove()[0], // our tile
  181. parent.getNodeMove()[0] - parent.getNodeMove()[1], // our steps
  182. parent.getNodeMove()[2], // our points
  183. nodeMove[0]) // opponent tile
  184. );
  185. }
  186. else {
  187. createMySubtree (child, depth+1, tile, nodeMove[0]);
  188. }
  189. }
  190. }
  191. /**
  192. * The main evaluation function
  193. * @param tile The current tile of the player
  194. * @param steps The total steps of the move
  195. * @param points The total points of the move
  196. * @param oppTile The tile of the best opponent
  197. * @return The evaluation of the roll
  198. */
  199. private double evaluate (int tile, int steps, int points, int oppTile) {
  200. if (tile > oppTile)
  201. return 0.65*steps + 0.35*points;
  202. else
  203. return 0.8*steps + 0.2*points - (oppTile - tile)*0.5;
  204. }
  205. /**
  206. * The Minimax recursive function for the maximizing part
  207. * @param s The current Node
  208. * @param a The alpha for pruning
  209. * @param b The beta for pruning
  210. * @return The selected Node
  211. */
  212. private Node MaxValue (Node s, double a, double b) {
  213. if (s.getChildren() == null)
  214. return s;
  215. else {
  216. Node r = null;
  217. double vv, v = Double.NEGATIVE_INFINITY;
  218. for (Node c : s.getChildren()) {
  219. if (r == null) r = c;
  220. vv = MinValue (c, a, b).getNodeEvaluation();
  221. //v = max (v, vv);
  222. if (vv > v) {
  223. v = vv;
  224. r = c;
  225. }
  226. if (vv >= b)
  227. return c; // prune
  228. a = max (a, vv);
  229. }
  230. return r;
  231. }
  232. }
  233. /**
  234. * The Minimax recursive function for the minimizing part
  235. * @param s The current Node
  236. * @param a The alpha for pruning
  237. * @param b The beta for pruning
  238. * @return The selected Node
  239. */
  240. private Node MinValue (Node s, double a, double b) {
  241. if (s.getChildren() == null)
  242. return s;
  243. else {
  244. Node r = null;
  245. double vv, v = Double.POSITIVE_INFINITY;
  246. for (Node c : s.getChildren()) {
  247. if (r == null) r = c;
  248. vv = MaxValue (c, a, b).getNodeEvaluation();
  249. //v = min (v, vv);
  250. if (vv < v) {
  251. v = vv;
  252. r = c;
  253. }
  254. if (vv <= a)
  255. return c; // prune
  256. b = min (b, vv);
  257. }
  258. return r;
  259. }
  260. }
  261. /** @return the minimum of x and y */
  262. private static double min (double x, double y) {
  263. return (x < y) ? x : y;
  264. }
  265. /** return the maximum of x and y */
  266. private static double max (double x, double y) {
  267. return (x > y) ? x : y;
  268. }
  269. /** @} */
  270. /** @name Data members package access only */
  271. /** @{ */
  272. private ArrayList<Integer[]> path; /**< Players history as required */
  273. /** @} */
  274. }