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.

452 lines
15 KiB

  1. package SnakePkg;
  2. import java.util.Random;
  3. /**
  4. * The game's board representation
  5. * @author Christos Choutouridis 8997
  6. */
  7. public class Board {
  8. /** @name Constructors */
  9. /** @{ */
  10. /** Default ctor */
  11. Board () {
  12. N = M =0;
  13. tiles = null;
  14. snakes = null;
  15. ladders = null;
  16. apples = null;
  17. }
  18. /**
  19. * Main constructor
  20. * We make some simple input checking before memory allocation.
  21. * @note
  22. * May throw BadBoardInput
  23. */
  24. Board (int N, int M, int numOfSnakes, int numOfLadders, int numOfApples)
  25. throws BadBoardInput {
  26. // Input checking
  27. if (numOfApples + numOfLadders*2 + numOfSnakes*2 >= N*M/2)
  28. throw new BadBoardInput("Too many Apples, Snakes and ladders");
  29. // Init the board object
  30. setN (N); // Input checked version (may throw)
  31. setM (M); // Input checked version (may throw)
  32. tiles = new int[N][M];
  33. snakes = new Snake[numOfSnakes];
  34. ladders = new Ladder[numOfLadders];
  35. apples = new Apple[numOfApples];
  36. }
  37. /**
  38. * Copy constructor. We make a deep copy of B and we trust B's
  39. * data to be valid
  40. * @note We don't use clone as long as we don't inherit Cloneable iface
  41. */
  42. Board (Board B) {
  43. this.N = B.getN();
  44. this.M = B.getM();
  45. tiles = new int[N][M];
  46. snakes = new Snake[B.getSnakes().length];
  47. ladders = new Ladder[B.getLadders().length];
  48. apples = new Apple[B.getApples().length];
  49. // Copy B's guts
  50. for (int i =0 ; i<N ; ++i)
  51. for (int j =0 ; j<M ; ++j)
  52. tiles[i][j] = B.getTiles()[i][j];
  53. for (int i =0 ; i<B.getSnakes().length ; ++i)
  54. snakes[i] = B.getSnakes()[i];
  55. for (int i =0 ; i<B.getLadders().length ; ++i)
  56. ladders[i] = B.getLadders()[i];
  57. for (int i =0 ; i<B.getApples().length ; ++i)
  58. apples[i] = B.getApples()[i];
  59. }
  60. /** @} */
  61. /** @name Get/Set interface */
  62. /** @{ */
  63. /** Get value N */
  64. int getN () { return N; }
  65. /**
  66. * Set value N
  67. * @throws BadBoardInput
  68. */
  69. void setN (int N) throws BadBoardInput {
  70. // Input checking
  71. if (N<=0)
  72. throw new BadBoardInput("N is zero or Less");
  73. this.N = N;
  74. }
  75. /** Get value M */
  76. int getM () { return M; }
  77. /**
  78. * Set value M
  79. * @throws BadBoardInput
  80. */
  81. void setM (int M) throws BadBoardInput {
  82. // Input checking
  83. if (M<=0)
  84. throw new BadBoardInput("M is zero or Less");
  85. this.M = M;
  86. }
  87. /** Get reference to tiles */
  88. int[][] getTiles () { return tiles; }
  89. /**
  90. * Set tiles (deep copy)
  91. * @param tiles Source of tiles to use
  92. * @throws BadBoardInput
  93. */
  94. void setTiles (int[][] tiles) throws BadBoardInput {
  95. // Input checking
  96. if (tiles.length != N && tiles[0].length != M)
  97. throw new BadBoardInput("tiles size missmatch");
  98. // Check if we need allocation
  99. if (this.tiles == null)
  100. this.tiles = new int[N][M];
  101. // Assign values (deep copy)
  102. for (int i =0 ; i<N ; ++i)
  103. for (int j =0 ; j<M ; ++j)
  104. this.tiles[i][j] = tiles[i][j];
  105. }
  106. /** Get reference to snakes */
  107. Snake[] getSnakes() { return snakes; }
  108. /**
  109. * Set snakes (deep copy)
  110. * @param snakes Source of snakes to use
  111. * @throws BadBoardInput
  112. * @note Requires Snake copy contructor
  113. */
  114. void setSnakes(Snake[] snakes) throws BadBoardInput {
  115. // Check if we need allocation
  116. if (this.snakes == null)
  117. this.snakes = new Snake[snakes.length];
  118. // Input checking
  119. if (this.snakes.length != snakes.length)
  120. throw new BadBoardInput("snakes size missmatch");
  121. // Assign values (deep copy)
  122. for (int i =0 ; i<this.snakes.length ; ++i)
  123. this.snakes[i] = new Snake(snakes[i]);
  124. }
  125. /** Get reference to ladders */
  126. Ladder[] getLadders() { return ladders; }
  127. /**
  128. * Set ladders (deep copy)
  129. * @param ladders Source of snakes to use
  130. * @throws BadBoardInput
  131. * @note Requires Ladder copy contructor
  132. */
  133. void setLadders (Ladder[] ladders) throws BadBoardInput {
  134. // Check if we need allocation
  135. if (this.ladders == null)
  136. this.ladders = new Ladder[ladders.length];
  137. // Input checking
  138. if (this.ladders.length != ladders.length)
  139. throw new BadBoardInput("ladders size missmatch");
  140. // Assign values (deep copy)
  141. for (int i =0 ; i<this.ladders.length ; ++i)
  142. this.ladders[i] = new Ladder(ladders[i]);
  143. }
  144. /** Get reference to apples */
  145. Apple[] getApples() { return apples; }
  146. /**
  147. * Set apples (deep copy)
  148. * @param apples Source of snakes to use
  149. * @throws BadBoardInput
  150. * @note Requires Apple copy contructor
  151. */
  152. void setApples (Apple[] apples) throws BadBoardInput {
  153. // Check if we need allocation
  154. if (this.apples == null)
  155. this.apples = new Apple[apples.length];
  156. // Input checking
  157. if (this.apples.length != apples.length)
  158. throw new BadBoardInput("ladders size missmatch");
  159. // Assign values (deep copy)
  160. for (int i =0 ; i<this.apples.length ; ++i)
  161. this.apples[i] = new Apple(apples[i]);
  162. }
  163. /** @} */
  164. /** @name Exposed API members */
  165. /** @{ */
  166. /**
  167. * Create a playable board for the game
  168. */
  169. void createBoard () {
  170. _tile_numbering (); // Create tile numbering
  171. _place_snakes (); // Place snakes
  172. _place_apples (); // Place Apples
  173. _place_ladders (); // place ladders
  174. }
  175. /**
  176. * make and PRINT element boards
  177. */
  178. void createElementBoard () {
  179. String[][] elementBoardSnakes = new String[N][M];
  180. String[][] elementBoardLadders = new String[N][M];
  181. String[][] elementBoardApples = new String[N][M];
  182. _makeElementSnakes (elementBoardSnakes);
  183. _makeElementLadders (elementBoardLadders);
  184. _makeElementApples (elementBoardApples);
  185. _printElement (elementBoardSnakes, "elementBoardSnakes");
  186. _printElement (elementBoardLadders, "elementBoardLadders");
  187. _printElement (elementBoardApples, "elementBoardApples");
  188. }
  189. /** @} */
  190. /** @name Private api */
  191. /**@{ */
  192. /**
  193. * @brief
  194. * Create the tile numbering
  195. * We use a starting point the tile[0][0] and as finish point
  196. * the tile[N-1][M-1]
  197. */
  198. private void _tile_numbering () {
  199. for (int i=0, tile =1 ; i<N ; ++i) {
  200. if (i%2 == 0) {
  201. // Even row, go right
  202. for (int j=0 ; j<M ; ++j)
  203. tiles[i][j] = tile++;
  204. }
  205. else {
  206. // Odd row, go left
  207. for (int j=M-1 ; j>=0 ; --j)
  208. tiles[i][j] = tile++;
  209. }
  210. }
  211. }
  212. /**
  213. * @brief
  214. * Place the snakes on the board
  215. * The only constrain at this point is that snake tails must be
  216. * below heads and in different tiles
  217. */
  218. private void _place_snakes () {
  219. int [] head = new int [snakes.length]; // temporary place holder for heads
  220. int [] tail = new int [snakes.length]; // temporary place holder for tails
  221. for (int i =0, tile =0 ; i<snakes.length ; ++i) {
  222. // Keep getting heads until they are different from the previous
  223. do
  224. tile = (int)(M + Math.random() * (N-1) * M); // Don't use first row for heads
  225. while (_search (head, tile) >= 0);
  226. head[i] = tile;
  227. tail[i] = (int)(Math.random() * (head[i] - head[i]%M)); // Don't use heads row and up for tail
  228. snakes[i] = new Snake(i, head[i], tail[i]); // Allocate snake
  229. }
  230. }
  231. /**
  232. * @brief
  233. * Place apples on the board
  234. * At this point we have snakes. The constrains here are
  235. * that apples have to lie on different tiles and not in some
  236. * snake's head
  237. */
  238. private void _place_apples () {
  239. int [] apple_tiles = new int [apples.length]; // temporary placeholder for heads
  240. int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
  241. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  242. snake_tiles[i] = snakes[i].getHeadId();
  243. for (int i =0, tile =0 ; i<apples.length ; ++i) {
  244. // Keep getting tiles until they are different from the previous
  245. // and not in some snake's head
  246. do
  247. tile = (int) (Math.random() * (N * M));
  248. while ((_search (apple_tiles, tile) >= 0) ||
  249. (_search (snake_tiles, tile) >= 0));
  250. apple_tiles[i] = tile;
  251. int points = (int)(Math.random() *10) * 5; // get points
  252. boolean red = (boolean)(Math.random() >=0.5); // get color
  253. // Allocate apple
  254. if (red)
  255. apples[i] = new Apple(i, tile, "red", points);
  256. else
  257. apples[i] = new Apple(i, tile, "black", -points);
  258. }
  259. }
  260. /**
  261. * @brief
  262. * Place ladders on board
  263. * At this point we have snakes and apples.
  264. * @note
  265. * We add a constrain for ladder so its up-setp has to be different from a
  266. * snake's head tile. This ensures the each ladder and snake are independent
  267. */
  268. private void _place_ladders () {
  269. int [] upstep = new int [ladders.length]; // temporary place holder for up-steps
  270. int [] downstep = new int [ladders.length]; // temporary place holder for down-step
  271. int [] snake_tiles = new int [snakes.length]; // array with snake head tiles
  272. for (int i =0 ; i<snakes.length ; ++i) // Load snake head tiles
  273. snake_tiles[i] = snakes[i].getHeadId();
  274. for (int i =0, tile =0 ; i<ladders.length ; ++i) {
  275. // Keep getting up-steps until they are different from the previous
  276. // and not in some snake's head
  277. do
  278. tile = (int)(M + Math.random() * (N-1) * M); // Don't use first row for heads
  279. while ((_search (upstep, tile) >= 0) ||
  280. (_search (snake_tiles, tile) >= 0));
  281. upstep[i] = tile;
  282. downstep[i] = (int)(Math.random() * (upstep[i] - upstep[i]%M));
  283. //^ Don't use up-step row and up for down-step
  284. ladders[i] = new Ladder (i, upstep[i], downstep[i]); // Allocate ladder
  285. }
  286. }
  287. /**
  288. * Make element array of snakes
  289. * @param elemSnakes
  290. */
  291. private void _makeElementSnakes (String[][] elemSnakes) {
  292. int [] head_tiles = new int [snakes.length]; // array with snake head tiles
  293. int [] tail_tiles = new int [snakes.length]; // array with snake head tiles
  294. int sn =-1;
  295. // Load snake head tiles
  296. for (int i =0 ; i<snakes.length ; ++i) {
  297. head_tiles[i] = snakes[i].getHeadId();
  298. tail_tiles[i] = snakes[i].getTailId();
  299. }
  300. // Search all tiles for snake heads and tails
  301. for (int i =0; i<N ; ++i) {
  302. for (int j =0 ; j<M ; ++j) {
  303. if ((sn = _search (head_tiles, tiles[i][j])) >= 0)
  304. elemSnakes[i][j] = "SH" + sn;
  305. else if ((sn = _search (tail_tiles, tiles[i][j])) >= 0)
  306. elemSnakes[i][j] = "ST" + sn;
  307. else
  308. elemSnakes[i][j] = "___";
  309. }
  310. }
  311. }
  312. /**
  313. * Make element array of ladders
  314. * @param elemLadders
  315. */
  316. private void _makeElementLadders (String[][] elemLadders) {
  317. int [] up_tiles = new int [ladders.length]; // array with ladder up-step tiles
  318. int [] down_tiles = new int [ladders.length]; // array with ladder down-step tiles
  319. int sn =-1;
  320. // Load ladder tiles
  321. for (int i =0 ; i<ladders.length ; ++i) {
  322. up_tiles[i] = ladders[i].getUpStepId();
  323. down_tiles[i] = ladders[i].getDownStepId();
  324. }
  325. // Search all tiles for snake heads and tails
  326. for (int i =0; i<N ; ++i) {
  327. for (int j =0 ; j<M ; ++j) {
  328. if ((sn = _search (up_tiles, tiles[i][j])) >= 0)
  329. elemLadders[i][j] = "LU" + sn;
  330. else if ((sn = _search (down_tiles, tiles[i][j])) >= 0)
  331. elemLadders[i][j] = "LD" + sn;
  332. else
  333. elemLadders[i][j] = "___";
  334. }
  335. }
  336. }
  337. /**
  338. * Make element array of apples
  339. * @param elemApples
  340. */
  341. private void _makeElementApples (String[][] elemApples) {
  342. int [] red_tiles = new int [apples.length]; // array with red apple tiles
  343. int [] black_tiles = new int [apples.length]; // array with black apple tiles
  344. int sn =-1;
  345. // Load apple tiles
  346. for (int i =0 ; i<apples.length ; ++i) {
  347. if (apples[i].getColor() == "red")
  348. red_tiles[i] = apples[i].getAppleTileId();
  349. else
  350. black_tiles[i] = apples[i].getAppleTileId();
  351. }
  352. // Search all tiles for snake heads and tails
  353. for (int i =0; i<N ; ++i) {
  354. for (int j =0 ; j<M ; ++j) {
  355. if ((sn = _search (red_tiles, tiles[i][j])) >= 0)
  356. elemApples[i][j] = "AR" + sn;
  357. else if ((sn = _search (black_tiles, tiles[i][j])) >= 0)
  358. elemApples[i][j] = "AB" + sn;
  359. else
  360. elemApples[i][j] = "___";
  361. }
  362. }
  363. }
  364. /**
  365. * Print element array
  366. * @param elem The element array to print
  367. * @param caption The caption
  368. * @note
  369. * As long as we use tiles[0][0] for first tile, this method
  370. * has to print in reverse Y-axis order. For ex:
  371. * 16 15 14 13
  372. * 09 10 11 12
  373. * 08 07 06 05
  374. * 01 02 03 04
  375. */
  376. private void _printElement (String[][] elem, String caption) {
  377. System.out.print(caption);
  378. for (int i=N-1 ; i>=0 ; --i) {
  379. System.out.println("");
  380. System.out.print(" ");
  381. for (int j =0 ; j<M ; ++j)
  382. System.out.print(elem[i][j] + " ");
  383. }
  384. System.out.println("");
  385. System.out.println("");
  386. }
  387. /** Search algorithm
  388. * @param array Array to search
  389. * @param elem Element of type T to find inside of array
  390. * @return The status of the operation
  391. * @arg -1 Element not found
  392. * @arg >=0 Element found
  393. */
  394. private int _search (int[] array, int elem) {
  395. for (int i=0 ; i<array.length ; ++i)
  396. if (elem == array[i])
  397. return i;
  398. return -1;
  399. }
  400. /**@} */
  401. /** @name Data members (private) */
  402. /** @{ */
  403. private int N; //!< Board's row count
  404. private int M; //!< Board's Column count
  405. private int[][] tiles; //!< Board's tiles
  406. private Snake[] snakes; //!< Board's snakes
  407. private Ladder[] ladders; //!< Board's ladders
  408. private Apple[] apples; //!< Board's apples
  409. /** @} */
  410. }
  411. class BadBoardInput extends Exception {
  412. BadBoardInput() {}
  413. BadBoardInput(String str) { super(str); }
  414. //static final long SerialVersionUIDAdder = 0;
  415. }