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.

557 lignes
20 KiB

  1. package net.hoo2.auth.dsproject.snake;
  2. import java.lang.Math;
  3. /**
  4. * @class Board
  5. * @brief The game's board representation.
  6. *
  7. * The board is a square collection of tiles numbered in a
  8. * boustrophedon (zig-zag) way. A number of snakes, ladders
  9. * and apples which we called elements are placed on the board
  10. * for each game.
  11. *
  12. * @author Christos Choutouridis AEM:8997
  13. * @email cchoutou@ece.auth.gr
  14. */
  15. public class Board {
  16. /** @name Constants */
  17. /**@{ */
  18. static final int POINTS_MAX = 20; /**< The maximum absolute number of points for each apple */
  19. static final int POINTS_STEP = 5; /**< The difference between different apple points */
  20. /**@} */
  21. /** @name Constructors */
  22. /** @{ */
  23. /** A doing nothing default constructor
  24. * @warining Avoid using this constructor as it requires all setters(or copy)
  25. * and @ref createBoard() to be called after.
  26. */
  27. Board () {
  28. N = M =0;
  29. tiles = null;
  30. snakes = null;
  31. ladders = null;
  32. apples = null;
  33. }
  34. /**
  35. * @brief Creates a board for game
  36. *
  37. * This constructor allocates the memory for the board and elements and
  38. * creates a board by placing all required elements on the board.
  39. *
  40. * @param N The row for the board
  41. * @param M The columns of the board
  42. * @param numOfSnakes Number of snakes to place
  43. * @param numOfLadders Number of ladders to place
  44. * @param numOfApples Number of Apples to place
  45. *
  46. * @warning
  47. * We call @ref createBoard() inside this constructor in order for
  48. * the board to be in "playable condition". This is preferable by the author.
  49. * A constructor should(if possible) to leave the object in a usable condition.
  50. * In order to follow the project requirements we create this functionality in a
  51. * separate function @ref createBoard(). We believe that if a user can make a
  52. * mistake he eventually will do it sometime. Here, if we leave the createBoard()
  53. * call to user we are enabling him to make it.
  54. */
  55. Board (int N, int M, int numOfSnakes, int numOfLadders, int numOfApples) {
  56. // Init the board object
  57. setN (N); // Input checked version (may throw)
  58. setM (M); // Input checked version (may throw)
  59. tiles = new int[N][M];
  60. snakes = new Snake[numOfSnakes];
  61. ladders = new Ladder[numOfLadders];
  62. apples = new Apple[numOfApples];
  63. createBoard (); // Complete board preparation and make all the element memory allocations
  64. }
  65. /**
  66. * @brief Copy constructor.
  67. * We make a deep copy of B and we trust B's data to be valid.
  68. * @param B The board we want to copy
  69. * @note We don't use clone as long as we don't inherit Cloneable iface
  70. * @note This requires Snake, Apple and Ladder copy constructors
  71. */
  72. Board (Board B) {
  73. N = B.getN();
  74. M = B.getM();
  75. tiles = new int[N][M];
  76. snakes = new Snake[B.getSnakes().length];
  77. ladders = new Ladder[B.getLadders().length];
  78. apples = new Apple[B.getApples().length];
  79. // Copy B's guts into new memory
  80. copyTiles(B.getTiles());
  81. copySnakes(B.getSnakes());
  82. copyLadders(B.getLadders());
  83. copyApples(B.getApples());
  84. }
  85. /** @} */
  86. /** @name Get/Set interface */
  87. /** @{ */
  88. /** Get value N */
  89. int getN () { return N; }
  90. /** Set value N */
  91. void setN (int N) { this.N = N; }
  92. /** Get value M */
  93. int getM () { return M; }
  94. /** Set value M */
  95. void setM (int M) { this.M = M; }
  96. /** Get reference to tiles */
  97. int[][] getTiles () { return tiles; }
  98. /**
  99. * Set tiles
  100. * @param tiles Source of tiles to use
  101. * @note This has to be called if the board is default constructed
  102. */
  103. void setTiles (int[][] tiles) { this.tiles = tiles; }
  104. /** Get reference to snakes */
  105. Snake[] getSnakes() { return snakes; }
  106. /**
  107. * Set snakes
  108. * @param snakes Reference to snakes to use
  109. * @note This requires snakes must be allocated elsewhere.
  110. */
  111. void setSnakes(Snake[] snakes) { this.snakes = snakes; }
  112. /** Get reference to ladders */
  113. Ladder[] getLadders() { return ladders; }
  114. /**
  115. * Set ladders
  116. * @param ladders Reference to ladders to use
  117. * @note This requires ladders must be allocated elsewhere.
  118. */
  119. void setLadders(Ladder[] ladders) { this.ladders = ladders; }
  120. /** Get reference to apples */
  121. Apple[] getApples() { return apples; }
  122. /**
  123. * Set apples
  124. * @param apples Reference to apples to use
  125. * @note This requires apples must be allocated elsewhere.
  126. */
  127. void setApples(Apple[] apples) { this.apples = apples; }
  128. /**
  129. * Copy tiles
  130. * @param tiles Source of tiles to use
  131. * @note This has to be called if the board is default constructed
  132. */
  133. void copyTiles (int[][] tiles) {
  134. // Check if we need allocation
  135. if (this.tiles == null)
  136. this.tiles = new int[N][M];
  137. // Copy-assign the values
  138. for (int i =0 ; i<N ; ++i)
  139. for (int j =0 ; j<M ; ++j)
  140. this.tiles[i][j] = tiles[i][j];
  141. }
  142. /**
  143. * Copy snakes (deep copy)
  144. * @param snakes Source of snakes to use
  145. * @note Requires Snake copy constructor
  146. * @note This has to be called if the board is default constructed
  147. */
  148. void copySnakes(Snake[] snakes) {
  149. // Check if we need allocation
  150. if (this.snakes == null)
  151. this.snakes = new Snake[snakes.length];
  152. // Assign values (deep copy)
  153. for (int i =0 ; i<this.snakes.length ; ++i)
  154. this.snakes[i] = new Snake(snakes[i]);
  155. }
  156. /**
  157. * Copy ladders (deep copy)
  158. * @param ladders Source of ladders to use
  159. * @note Requires Ladder copy constructor
  160. * @note This has to be called if the board is default constructed
  161. */
  162. void copyLadders (Ladder[] ladders) {
  163. // Check if we need allocation
  164. if (this.ladders == null)
  165. this.ladders = new Ladder[ladders.length];
  166. // Assign values (deep copy)
  167. for (int i =0 ; i<this.ladders.length ; ++i)
  168. this.ladders[i] = new Ladder(ladders[i]);
  169. }
  170. /**
  171. * Copy apples (deep copy)
  172. * @param apples Source of apples to use
  173. * @note Requires Apple copy constructor
  174. * @note This has to be called if the board is default constructed
  175. */
  176. void copyApples (Apple[] apples) {
  177. // Check if we need allocation
  178. if (this.apples == null)
  179. this.apples = new Apple[apples.length];
  180. // Assign values (deep copy)
  181. for (int i =0 ; i<this.apples.length ; ++i)
  182. this.apples[i] = new Apple(apples[i]);
  183. }
  184. /** @} */
  185. /** @name Exposed API members */
  186. /** @{ */
  187. /**
  188. * Check if the tile is a snake head. If so return the snake's
  189. * tails tile. If not return the same tile
  190. * @param tile The tile to check
  191. * @return The result tile
  192. */
  193. int checkSnake (int tile) {
  194. for (int i =0 ; i<snakes.length ; ++i) {
  195. if (snakes[i].getHeadId() == tile)
  196. return snakes[i].getTailId();
  197. }
  198. return tile;
  199. }
  200. /**
  201. * Check if the tile is a ladder down step. If so return the ladder's
  202. * up step tile. If not return the same tile.
  203. * @note
  204. * This also break the ladder if used
  205. * @param tile The tile to check
  206. * @return The result tile
  207. */
  208. int checkLadder (int tile) {
  209. for (int i =0 ; i<ladders.length ; ++i) {
  210. if (ladders[i].getDownStepId() == tile &&
  211. ladders[i].getBroken() == false) {
  212. ladders[i].setBroken(true);
  213. return ladders[i].getUpStepId();
  214. }
  215. }
  216. return tile;
  217. }
  218. /**
  219. * Check if the tile is an apple tile. If so eat it and return the score difference
  220. * @param tile The tile to check
  221. * @return The score difference
  222. */
  223. int checkApple (int tile) {
  224. int ds =0; // delta-score
  225. for (int i =0 ; i<apples.length ; ++i) {
  226. if (apples[i].getAppleTileId() == tile) {
  227. // eat it
  228. ds = apples[i].getPoints();
  229. apples[i].setPoints(0);
  230. }
  231. }
  232. return ds;
  233. }
  234. /**
  235. * Create a playable board for the game.
  236. * @warning
  237. * This is not required to be called after construction in order to ensure board's playable
  238. * condition. In fact this function SHOULD NOT CALLED AT ALL.
  239. * The project requirements expect this to be public. The preferable mode would be private.
  240. * @see Board() constructor.
  241. */
  242. void createBoard () {
  243. _tileNumbering (); // Create tile numbering
  244. _placeSnakes (); // Place snakes
  245. _placeApples (); // Place Apples
  246. _placeLadders (); // place ladders
  247. }
  248. /**
  249. * @brief
  250. * make and print element boards
  251. * This is not required in order for the board to be playable
  252. * It just produce a stdout output for convenience.
  253. */
  254. void createElementBoard () {
  255. String[][] elementBoardSnakes = new String[N][M];
  256. String[][] elementBoardLadders = new String[N][M];
  257. String[][] elementBoardApples = new String[N][M];
  258. _makeElementSnakes (elementBoardSnakes);
  259. _makeElementLadders (elementBoardLadders);
  260. _makeElementApples (elementBoardApples);
  261. _printElement (elementBoardSnakes, "elementBoardSnakes");
  262. _printElement (elementBoardLadders, "elementBoardLadders");
  263. _printElement (elementBoardApples, "elementBoardApples");
  264. }
  265. /** @} */
  266. /** @name Private api */
  267. /**@{ */
  268. /**
  269. * @brief
  270. * Create the tile numbering in a boustrophedon (zig-zag) way.
  271. * We use a starting point the tile[0][0] and as finish point
  272. * the tile[N-1][M-1]
  273. */
  274. private void _tileNumbering () {
  275. for (int i=0, tile =1 ; i<N ; ++i) {
  276. if (i%2 == 0) {
  277. // Even row, go right
  278. for (int j=0 ; j<M ; ++j)
  279. tiles[i][j] = tile++;
  280. }
  281. else {
  282. // Odd row, go left
  283. for (int j=M-1 ; j>=0 ; --j)
  284. tiles[i][j] = tile++;
  285. }
  286. }
  287. }
  288. /**
  289. * @brief
  290. * Place the snakes on the board
  291. * The only constrain at this point is that snake tails must be placed
  292. * below heads and heads must be placed in separate tiles
  293. */
  294. private void _placeSnakes () {
  295. int [] head = new int [snakes.length]; // temporary place holder for heads
  296. int [] tail = new int [snakes.length]; // temporary place holder for tails
  297. for (int i =0, tile =0 ; i<snakes.length ; ++i) {
  298. // Keep getting heads until they are different from the previous
  299. do
  300. tile = _pickRandom (M+1, M*N); // Don't use first row for heads
  301. while (_search (head, tile) >= 0);
  302. head[i] = tile;
  303. tail[i] = _pickRandom (1, head[i]-head[i]%M); // Don't use heads row and up for tail
  304. snakes[i] = new Snake(i, head[i], tail[i]); // Allocate snake
  305. }
  306. }
  307. /**
  308. * @brief
  309. * Place apples on the board
  310. * The constrains here are
  311. * that apples have to lie on different tiles and not in some
  312. * snake's head.
  313. * @note We require we have snakes.
  314. */
  315. private void _placeApples () {
  316. int [] apple_tiles = new int [apples.length]; // temporary placeholder for apples
  317. int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
  318. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  319. snake_tiles[i] = snakes[i].getHeadId();
  320. for (int i =0, tile =0 ; i<apples.length ; ++i) {
  321. // Keep getting tiles until they are different from the previous
  322. // and not in some snake's head
  323. do
  324. tile = _pickRandom (1, M*N);
  325. while ((_search (apple_tiles, tile) >= 0) ||
  326. (_search (snake_tiles, tile) >= 0));
  327. apple_tiles[i] = tile;
  328. // get points
  329. int points = _pickRandom (1, (POINTS_MAX/POINTS_STEP)) * POINTS_STEP;
  330. boolean red = (boolean)(Math.random() >=0.5); // get color
  331. // Allocate apple
  332. if (red)
  333. apples[i] = new Apple(i, tile, "red", points);
  334. else
  335. apples[i] = new Apple(i, tile, "black", -points);
  336. }
  337. }
  338. /**
  339. * @brief
  340. * Place ladders on board
  341. *
  342. * We add constrains so each ladder's up-step tile has to be different from:
  343. * * A snake's head tile. This ensures ladders and snakes are independent
  344. * * A ladders's down-step. This ensure we eliminate ladder chains.
  345. * * One other ladder's up-step. This is not critical but helps the printElement functionality
  346. *
  347. * We add constrains so each ladder's down-step tile has to be different from:
  348. * * A snake's head tile. This ensures ladders and snakes are independent
  349. * * A ladders's down-step. This is not critical but helps the printElement functionality
  350. * * One other ladder's up-step. This ensure we eliminate ladder chains.
  351. * @note We require we have snakes.
  352. */
  353. private void _placeLadders () {
  354. int [] up_step = new int [ladders.length]; // temporary place holder for up-steps
  355. int [] down_step = new int [ladders.length]; // temporary place holder for down-step
  356. int [] snake_tiles= new int [snakes.length]; // array with snake head tiles
  357. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  358. snake_tiles[i] = snakes[i].getHeadId();
  359. for (int i =0, tile =0 ; i<ladders.length ; ++i) {
  360. // Keep getting up-steps until they are different from the previous ladder tiles
  361. // and not in some snake's head
  362. do
  363. tile = _pickRandom (M+1, M*N); // Don't use first row for up-steps
  364. while ((_search (up_step, tile) >= 0)
  365. || (_search (down_step, tile) >= 0)
  366. || (_search (snake_tiles, tile) >= 0));
  367. up_step[i] = tile;
  368. // Keep getting down-steps until they are different from the previous ladder tiles
  369. // and not in some snake's head
  370. do
  371. // Don't use up-step row and up for down-step
  372. tile = _pickRandom (1, up_step[i]-up_step[i]%M);
  373. while ((_search (up_step, tile) >= 0)
  374. || (_search (down_step, tile) >= 0)
  375. || (_search (snake_tiles, tile) >= 0));
  376. down_step[i] = tile;
  377. ladders[i] = new Ladder (i, up_step[i], down_step[i]); // Allocate ladder
  378. }
  379. }
  380. /**
  381. * Make element array of snakes as required by the project
  382. * @param elemSnakes
  383. */
  384. private void _makeElementSnakes (String[][] elemSnakes) {
  385. int [] head_tiles = new int [snakes.length]; // array with snake head tiles
  386. int [] tail_tiles = new int [snakes.length]; // array with snake head tiles
  387. int sn =-1;
  388. // Load snake head tiles
  389. for (int i =0 ; i<snakes.length ; ++i) {
  390. head_tiles[i] = snakes[i].getHeadId();
  391. tail_tiles[i] = snakes[i].getTailId();
  392. }
  393. // Search all tiles for snake heads and tails
  394. for (int i =0; i<N ; ++i) {
  395. for (int j =0 ; j<M ; ++j) {
  396. if ((sn = _search (head_tiles, tiles[i][j])) >= 0)
  397. elemSnakes[i][j] = "SH" + sn;
  398. else if ((sn = _search (tail_tiles, tiles[i][j])) >= 0)
  399. elemSnakes[i][j] = "ST" + sn;
  400. else
  401. elemSnakes[i][j] = "___";
  402. }
  403. }
  404. }
  405. /**
  406. * Make element array of ladders as required by the project
  407. * @param elemLadders
  408. */
  409. private void _makeElementLadders (String[][] elemLadders) {
  410. int [] up_tiles = new int [ladders.length]; // array with ladder up-step tiles
  411. int [] down_tiles = new int [ladders.length]; // array with ladder down-step tiles
  412. int sn =-1;
  413. // Load ladder tiles
  414. for (int i =0 ; i<ladders.length ; ++i) {
  415. up_tiles[i] = ladders[i].getUpStepId();
  416. down_tiles[i] = ladders[i].getDownStepId();
  417. }
  418. // Search all tiles for snake heads and tails
  419. for (int i =0; i<N ; ++i) {
  420. for (int j =0 ; j<M ; ++j) {
  421. if ((sn = _search (up_tiles, tiles[i][j])) >= 0)
  422. elemLadders[i][j] = "LU" + sn;
  423. else if ((sn = _search (down_tiles, tiles[i][j])) >= 0)
  424. elemLadders[i][j] = "LD" + sn;
  425. else
  426. elemLadders[i][j] = "___";
  427. }
  428. }
  429. }
  430. /**
  431. * Make element array of apples as required by the project
  432. * @param elemApples
  433. */
  434. private void _makeElementApples (String[][] elemApples) {
  435. int [] red_tiles = new int [apples.length]; // array with red apple tiles
  436. int [] black_tiles = new int [apples.length]; // array with black apple tiles
  437. int sn =-1;
  438. // Load apple tiles
  439. for (int i =0 ; i<apples.length ; ++i) {
  440. if (apples[i].getColor() == "red")
  441. red_tiles[i] = apples[i].getAppleTileId();
  442. else
  443. black_tiles[i] = apples[i].getAppleTileId();
  444. }
  445. // Search all tiles for snake heads and tails
  446. for (int i =0; i<N ; ++i) {
  447. for (int j =0 ; j<M ; ++j) {
  448. if ((sn = _search (red_tiles, tiles[i][j])) >= 0)
  449. elemApples[i][j] = "AR" + sn;
  450. else if ((sn = _search (black_tiles, tiles[i][j])) >= 0)
  451. elemApples[i][j] = "AB" + sn;
  452. else
  453. elemApples[i][j] = "___";
  454. }
  455. }
  456. }
  457. /**
  458. * Print element array
  459. * @param elem The element array to print
  460. * @param caption The caption
  461. * @note
  462. * As long as we use tiles[0][0] for first tile, this method
  463. * has to print in reverse Y-axis order. For ex:
  464. * <pre>
  465. * 16 15 14 13
  466. * 09 10 11 12
  467. * 08 07 06 05
  468. * 01 02 03 04
  469. * </pre>
  470. */
  471. private void _printElement (String[][] elem, String caption) {
  472. System.out.print(caption);
  473. for (int i=N-1 ; i>=0 ; --i) {
  474. System.out.println("");
  475. System.out.print(" ");
  476. for (int j =0 ; j<M ; ++j)
  477. System.out.print(elem[i][j] + " ");
  478. }
  479. System.out.println("");
  480. System.out.println("");
  481. }
  482. /**
  483. * Pick a random tile in range [from..to]
  484. * @param from The first tile to consider
  485. * @param to The last tile to consider
  486. * @return The random pick
  487. */
  488. private int _pickRandom (int from, int to) {
  489. return from + (int)(Math.random() * (to - from));
  490. }
  491. /** Search algorithm
  492. * @param array Array to search
  493. * @param elem Element of type T to find inside of array
  494. * @return The status of the operation
  495. * @arg -1 Element not found
  496. * @arg >=0 Element found
  497. */
  498. private int _search (int[] array, int elem) {
  499. for (int i=0 ; i<array.length ; ++i)
  500. if (elem == array[i])
  501. return i;
  502. return -1;
  503. }
  504. /**@} */
  505. /** @name Data members */
  506. /** @{ */
  507. private int N; /**< Board's row count */
  508. private int M; /**< Board's Column count */
  509. private int[][] tiles; /**< Board's tiles */
  510. private Snake[] snakes; /**< Board's snakes */
  511. private Ladder[] ladders; /**< Board's ladders */
  512. private Apple[] apples; /**< Board's apples */
  513. /** @} */
  514. }