A java snake game 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.

291 lines
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. }