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.

558 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, boolean climb) {
  209. for (int i =0 ; i<ladders.length ; ++i) {
  210. if (ladders[i].getDownStepId() == tile &&
  211. ladders[i].getBroken() == false) {
  212. ladders[i].setBroken(climb);
  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, boolean eat) {
  224. int ds =0; // delta-score
  225. for (int i =0 ; i<apples.length ; ++i) {
  226. if (apples[i].getAppleTileId() == tile) {
  227. ds = apples[i].getPoints();
  228. // eat it
  229. if (eat)
  230. apples[i].setPoints(0);
  231. }
  232. }
  233. return ds;
  234. }
  235. /**
  236. * Create a playable board for the game.
  237. * @warning
  238. * This is not required to be called after construction in order to ensure board's playable
  239. * condition. In fact this function SHOULD NOT CALLED AT ALL.
  240. * The project requirements expect this to be public. The preferable mode would be private.
  241. * @see Board() constructor.
  242. */
  243. void createBoard () {
  244. _tileNumbering (); // Create tile numbering
  245. _placeSnakes (); // Place snakes
  246. _placeApples (); // Place Apples
  247. _placeLadders (); // place ladders
  248. }
  249. /**
  250. * @brief
  251. * make and print element boards
  252. * This is not required in order for the board to be playable
  253. * It just produce a stdout output for convenience.
  254. */
  255. void createElementBoard () {
  256. String[][] elementBoardSnakes = new String[N][M];
  257. String[][] elementBoardLadders = new String[N][M];
  258. String[][] elementBoardApples = new String[N][M];
  259. _makeElementSnakes (elementBoardSnakes);
  260. _makeElementLadders (elementBoardLadders);
  261. _makeElementApples (elementBoardApples);
  262. _printElement (elementBoardSnakes, "elementBoardSnakes");
  263. _printElement (elementBoardLadders, "elementBoardLadders");
  264. _printElement (elementBoardApples, "elementBoardApples");
  265. }
  266. /** @} */
  267. /** @name Private api */
  268. /**@{ */
  269. /**
  270. * @brief
  271. * Create the tile numbering in a boustrophedon (zig-zag) way.
  272. * We use a starting point the tile[0][0] and as finish point
  273. * the tile[N-1][M-1]
  274. */
  275. private void _tileNumbering () {
  276. for (int i=0, tile =1 ; i<N ; ++i) {
  277. if (i%2 == 0) {
  278. // Even row, go right
  279. for (int j=0 ; j<M ; ++j)
  280. tiles[i][j] = tile++;
  281. }
  282. else {
  283. // Odd row, go left
  284. for (int j=M-1 ; j>=0 ; --j)
  285. tiles[i][j] = tile++;
  286. }
  287. }
  288. }
  289. /**
  290. * @brief
  291. * Place the snakes on the board
  292. * The only constrain at this point is that snake tails must be placed
  293. * below heads and heads must be placed in separate tiles
  294. */
  295. private void _placeSnakes () {
  296. int [] head = new int [snakes.length]; // temporary place holder for heads
  297. int [] tail = new int [snakes.length]; // temporary place holder for tails
  298. for (int i =0, tile =0 ; i<snakes.length ; ++i) {
  299. // Keep getting heads until they are different from the previous
  300. do
  301. tile = _pickRandom (M+1, M*N); // Don't use first row for heads
  302. while (_search (head, tile) >= 0);
  303. head[i] = tile;
  304. tail[i] = _pickRandom (1, head[i]-head[i]%M); // Don't use heads row and up for tail
  305. snakes[i] = new Snake(i, head[i], tail[i]); // Allocate snake
  306. }
  307. }
  308. /**
  309. * @brief
  310. * Place apples on the board
  311. * The constrains here are
  312. * that apples have to lie on different tiles and not in some
  313. * snake's head.
  314. * @note We require we have snakes.
  315. */
  316. private void _placeApples () {
  317. int [] apple_tiles = new int [apples.length]; // temporary placeholder for apples
  318. int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
  319. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  320. snake_tiles[i] = snakes[i].getHeadId();
  321. for (int i =0, tile =0 ; i<apples.length ; ++i) {
  322. // Keep getting tiles until they are different from the previous
  323. // and not in some snake's head
  324. do
  325. tile = _pickRandom (1, M*N);
  326. while ((_search (apple_tiles, tile) >= 0) ||
  327. (_search (snake_tiles, tile) >= 0));
  328. apple_tiles[i] = tile;
  329. // get points
  330. int points = _pickRandom (1, (POINTS_MAX/POINTS_STEP)) * POINTS_STEP;
  331. boolean red = (boolean)(Math.random() >=0.5); // get color
  332. // Allocate apple
  333. if (red)
  334. apples[i] = new Apple(i, tile, "red", points);
  335. else
  336. apples[i] = new Apple(i, tile, "black", -points);
  337. }
  338. }
  339. /**
  340. * @brief
  341. * Place ladders on board
  342. *
  343. * We add constrains so each ladder's up-step tile has to be different from:
  344. * * A snake's head tile. This ensures ladders and snakes are independent
  345. * * A ladders's down-step. This ensure we eliminate ladder chains.
  346. * * One other ladder's up-step. This is not critical but helps the printElement functionality
  347. *
  348. * We add constrains so each ladder's down-step tile has to be different from:
  349. * * A snake's head tile. This ensures ladders and snakes are independent
  350. * * A ladders's down-step. This is not critical but helps the printElement functionality
  351. * * One other ladder's up-step. This ensure we eliminate ladder chains.
  352. * @note We require we have snakes.
  353. */
  354. private void _placeLadders () {
  355. int [] up_step = new int [ladders.length]; // temporary place holder for up-steps
  356. int [] down_step = new int [ladders.length]; // temporary place holder for down-step
  357. int [] snake_tiles= new int [snakes.length]; // array with snake head tiles
  358. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  359. snake_tiles[i] = snakes[i].getHeadId();
  360. for (int i =0, tile =0 ; i<ladders.length ; ++i) {
  361. // Keep getting up-steps until they are different from the previous ladder tiles
  362. // and not in some snake's head
  363. do
  364. tile = _pickRandom (M+1, M*N); // Don't use first row for up-steps
  365. while ((_search (up_step, tile) >= 0)
  366. || (_search (down_step, tile) >= 0)
  367. || (_search (snake_tiles, tile) >= 0));
  368. up_step[i] = tile;
  369. // Keep getting down-steps until they are different from the previous ladder tiles
  370. // and not in some snake's head
  371. do
  372. // Don't use up-step row and up for down-step
  373. tile = _pickRandom (1, up_step[i]-up_step[i]%M);
  374. while ((_search (up_step, tile) >= 0)
  375. || (_search (down_step, tile) >= 0)
  376. || (_search (snake_tiles, tile) >= 0));
  377. down_step[i] = tile;
  378. ladders[i] = new Ladder (i, up_step[i], down_step[i]); // Allocate ladder
  379. }
  380. }
  381. /**
  382. * Make element array of snakes as required by the project
  383. * @param elemSnakes
  384. */
  385. private void _makeElementSnakes (String[][] elemSnakes) {
  386. int [] head_tiles = new int [snakes.length]; // array with snake head tiles
  387. int [] tail_tiles = new int [snakes.length]; // array with snake head tiles
  388. int sn =-1;
  389. // Load snake head tiles
  390. for (int i =0 ; i<snakes.length ; ++i) {
  391. head_tiles[i] = snakes[i].getHeadId();
  392. tail_tiles[i] = snakes[i].getTailId();
  393. }
  394. // Search all tiles for snake heads and tails
  395. for (int i =0; i<N ; ++i) {
  396. for (int j =0 ; j<M ; ++j) {
  397. if ((sn = _search (head_tiles, tiles[i][j])) >= 0)
  398. elemSnakes[i][j] = "SH" + sn;
  399. else if ((sn = _search (tail_tiles, tiles[i][j])) >= 0)
  400. elemSnakes[i][j] = "ST" + sn;
  401. else
  402. elemSnakes[i][j] = "___";
  403. }
  404. }
  405. }
  406. /**
  407. * Make element array of ladders as required by the project
  408. * @param elemLadders
  409. */
  410. private void _makeElementLadders (String[][] elemLadders) {
  411. int [] up_tiles = new int [ladders.length]; // array with ladder up-step tiles
  412. int [] down_tiles = new int [ladders.length]; // array with ladder down-step tiles
  413. int sn =-1;
  414. // Load ladder tiles
  415. for (int i =0 ; i<ladders.length ; ++i) {
  416. up_tiles[i] = ladders[i].getUpStepId();
  417. down_tiles[i] = ladders[i].getDownStepId();
  418. }
  419. // Search all tiles for snake heads and tails
  420. for (int i =0; i<N ; ++i) {
  421. for (int j =0 ; j<M ; ++j) {
  422. if ((sn = _search (up_tiles, tiles[i][j])) >= 0)
  423. elemLadders[i][j] = "LU" + sn;
  424. else if ((sn = _search (down_tiles, tiles[i][j])) >= 0)
  425. elemLadders[i][j] = "LD" + sn;
  426. else
  427. elemLadders[i][j] = "___";
  428. }
  429. }
  430. }
  431. /**
  432. * Make element array of apples as required by the project
  433. * @param elemApples
  434. */
  435. private void _makeElementApples (String[][] elemApples) {
  436. int [] red_tiles = new int [apples.length]; // array with red apple tiles
  437. int [] black_tiles = new int [apples.length]; // array with black apple tiles
  438. int sn =-1;
  439. // Load apple tiles
  440. for (int i =0 ; i<apples.length ; ++i) {
  441. if (apples[i].getColor() == "red")
  442. red_tiles[i] = apples[i].getAppleTileId();
  443. else
  444. black_tiles[i] = apples[i].getAppleTileId();
  445. }
  446. // Search all tiles for snake heads and tails
  447. for (int i =0; i<N ; ++i) {
  448. for (int j =0 ; j<M ; ++j) {
  449. if ((sn = _search (red_tiles, tiles[i][j])) >= 0)
  450. elemApples[i][j] = "AR" + sn;
  451. else if ((sn = _search (black_tiles, tiles[i][j])) >= 0)
  452. elemApples[i][j] = "AB" + sn;
  453. else
  454. elemApples[i][j] = "___";
  455. }
  456. }
  457. }
  458. /**
  459. * Print element array
  460. * @param elem The element array to print
  461. * @param caption The caption
  462. * @note
  463. * As long as we use tiles[0][0] for first tile, this method
  464. * has to print in reverse Y-axis order. For ex:
  465. * <pre>
  466. * 16 15 14 13
  467. * 09 10 11 12
  468. * 08 07 06 05
  469. * 01 02 03 04
  470. * </pre>
  471. */
  472. private void _printElement (String[][] elem, String caption) {
  473. System.out.print(caption);
  474. for (int i=N-1 ; i>=0 ; --i) {
  475. System.out.println("");
  476. System.out.print(" ");
  477. for (int j =0 ; j<M ; ++j)
  478. System.out.print(elem[i][j] + " ");
  479. }
  480. System.out.println("");
  481. System.out.println("");
  482. }
  483. /**
  484. * Pick a random tile in range [from..to]
  485. * @param from The first tile to consider
  486. * @param to The last tile to consider
  487. * @return The random pick
  488. */
  489. private int _pickRandom (int from, int to) {
  490. return from + (int)(Math.random() * (to - from));
  491. }
  492. /** Search algorithm
  493. * @param array Array to search
  494. * @param elem Element of type T to find inside of array
  495. * @return The status of the operation
  496. * @arg -1 Element not found
  497. * @arg >=0 Element found
  498. */
  499. private int _search (int[] array, int elem) {
  500. for (int i=0 ; i<array.length ; ++i)
  501. if (elem == array[i])
  502. return i;
  503. return -1;
  504. }
  505. /**@} */
  506. /** @name Data members */
  507. /** @{ */
  508. private int N; /**< Board's row count */
  509. private int M; /**< Board's Column count */
  510. private int[][] tiles; /**< Board's tiles */
  511. private Snake[] snakes; /**< Board's snakes */
  512. private Ladder[] ladders; /**< Board's ladders */
  513. private Apple[] apples; /**< Board's apples */
  514. /** @} */
  515. }