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.

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