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.

570 lignes
20 KiB

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