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.

261 lines
9.8 KiB

  1. /**
  2. * @file Board.java
  3. *
  4. * @author Christos Choutouridis AEM:8997
  5. * @email cchoutou@ece.auth.gr
  6. */
  7. package net.hoo2.auth.labyrinth;
  8. import java.util.function.IntFunction;
  9. /**
  10. * @brief
  11. * This class is the representation of the games's board
  12. *
  13. * The board is the square arrangement of the tiles. This class is also
  14. * the owner of the tile and supply objects.
  15. */
  16. public class Board {
  17. /** @name Constructors */
  18. /** @{ */
  19. /**
  20. * The empty constructor for default initialization
  21. */
  22. protected Board() {
  23. this.N = this.S = this.W = 0;
  24. this.tiles = null;
  25. this.supplies =null;
  26. }
  27. /**
  28. * The main constructor for the application
  29. * @param N The size of each edge of the board
  30. * @param S The number of supplies on the board
  31. * @param W The number of walls on the board
  32. */
  33. protected Board(int N, int S, int W) {
  34. assert (W>= 4*N-1 && W<=(3*N*N+1)/2 )
  35. : "Boards walls has to be in the range [4N-1, (3N^2+1)/2]";
  36. this.N = Session.boardSize = N;
  37. this.S = S;
  38. this.W = W;
  39. tiles = new Tile[N*N];
  40. supplies = new Supply[S];
  41. }
  42. /**
  43. * Deep copy constructor
  44. * @param b The board to copy
  45. *
  46. * @note
  47. * The lack of value semantics in java is (in author's opinion) one of the greatest
  48. * weakness of the language and one of the reasons why it will never be a language
  49. * to care about. To quote Alexander Stepanof's words in "elements of programming" section 1.5:
  50. * "Assignment is a procedure that takes two objects of the same type and makes the first
  51. * object equal to the second without modifying the second".
  52. * In this class we try to cope with this situation knowing that we can not do anything about
  53. * assignment operator. We just add value semantics to the copy constructor and go on with our lifes...
  54. */
  55. protected Board(Board b) {
  56. // Copy primitives
  57. this.N = b.N;
  58. this.S = b.S;
  59. this.W = b.W;
  60. // Clone arrays
  61. this.tiles = b.tiles.clone();
  62. this.supplies = b.supplies.clone();
  63. }
  64. /** @} */
  65. /** @name Supply's main application interface */
  66. /** @{ */
  67. protected void createTiles() {
  68. int wallPool = W;
  69. wallPool -= createBasicTileWalls ();
  70. createInnerWalls(wallPool);
  71. }
  72. protected void createSupplies(int theseusTile, int minotaurTile) {
  73. ShuffledRange rand = new ShuffledRange(0, N*N);
  74. for (int tileId, i=0 ; i<supplies.length ; ++i) {
  75. do
  76. tileId = rand.get();
  77. while (tileId == theseusTile || tileId == minotaurTile);
  78. supplies[i] = new Supply(i, tileId);
  79. }
  80. }
  81. protected void createBoard(int theseusTile, int minotaurTile) {
  82. createTiles();
  83. createSupplies(theseusTile, minotaurTile);
  84. }
  85. protected String[][] getStringRepresentation(int theseusTile, int minotaurTile) {
  86. String[][] frame = new String[2*N+1][N];
  87. for (int row=0 ; row<N ; ++row) {
  88. int col;
  89. for (col =0 ; col<N-1 ; ++col)
  90. renderTile(frame, row, col, theseusTile, minotaurTile);
  91. renderSentinelTile(frame, row, col, theseusTile, minotaurTile);
  92. }
  93. return frame;
  94. }
  95. protected void printBoard (String[][] sBoard) {
  96. for (int i=sBoard.length-1 ; i>=0 ; --i) {
  97. for (String it : sBoard[i])
  98. System.out.print(it);
  99. System.out.println();
  100. }
  101. }
  102. int size() { return N; }
  103. /** @} */
  104. /**
  105. * @name Accessor/Mutator interface
  106. * @note
  107. * Please consider not to use mutator interface. Its the abstraction killer :(
  108. */
  109. /** @{ */
  110. protected int getN() { return N; }
  111. protected int getS() { return S; }
  112. protected int getW() { return W; }
  113. /**
  114. * @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure".
  115. * <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a>
  116. * @return Reference to inner tiles array.
  117. */
  118. protected Tile[] getTiles() { return tiles; }
  119. /**
  120. * @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure".
  121. * <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a>
  122. * @return Reference to inner supplies array.
  123. */
  124. protected Supply[] getSupplies() { return supplies; }
  125. protected void setN(int N) { this.N = N; }
  126. protected void setS(int S) { this.S = S; }
  127. protected void setW(int W) { this.W = W; }
  128. /**
  129. * @param tiles Reference to tiles that we want to act as replacement for the inner tiles array.
  130. * @note Use with care.
  131. * Any call to this function will probably add memory for the garbage collector.
  132. */
  133. protected void setTiles(Tile[] tiles) { this.tiles = tiles; }
  134. /**
  135. * @param supplies Reference to supplies that we want to act as replacement for the inner supplies array.
  136. * @note Use with care.
  137. * Any call to this function will probably add memory for the garbage collector.
  138. */
  139. protected void setSupplies(Supply[] supplies) { this.supplies= supplies; }
  140. /** @} */
  141. private int createBasicTileWalls () {
  142. int tileCount =0;
  143. // Create basic tiles and outer walls
  144. for (int i =0 ; i< tiles.length ; ++i) {
  145. int r = Position.toRow(i);
  146. int c = Position.toCol(i);
  147. boolean up = (r == N-1) ? true : false;
  148. boolean down = (r == 0 && c != 0) ? true : false;
  149. boolean left = (c == 0) ? true : false;
  150. boolean right = (c == N-1) ? true : false;
  151. tileCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0));
  152. tiles[i] = new Tile (i, up, down, left, right);
  153. }
  154. return tileCount;
  155. }
  156. private int createInnerWalls (int wallPool) {
  157. // Create inner walls for the rest of the desired walls
  158. ShuffledRange randTiles = new ShuffledRange(0, N*N);
  159. for (int tileId, i =0 ; i<wallPool ; ++i) {
  160. // randomly pick a tile with less than 2 walls
  161. do {
  162. tileId = randTiles.get();
  163. if (tileId == Const.noTileId)
  164. return 0;
  165. } while (tiles[tileId].hasWalls() >= 2); //XXX: Predicate : can put wall
  166. // Randomly pick a not used direction in that tile
  167. ShuffledRange randDirections = new ShuffledRange(Direction.Begin, Direction.Step, Direction.End);
  168. Position tilePos = new Position(tileId);
  169. int dir;
  170. do
  171. dir = randDirections.get();
  172. while (tiles[tileId].hasWall(dir));
  173. switch (dir) {
  174. case Direction.UP:
  175. tiles[tileId].setWalls(true, false, false, false);
  176. tiles[Position.toID(tilePos.getRow()+1, tilePos.getCol())].setWalls(false, true, false, false);
  177. break;
  178. case Direction.DOWN:
  179. tiles[tileId].setWalls(false, true, false, false);
  180. tiles[Position.toID(tilePos.getRow()-1, tilePos.getCol())].setWalls(true, false, false, false);
  181. break;
  182. case Direction.LEFT:
  183. tiles[tileId].setWalls(false, false, true, false);
  184. tiles[Position.toID(tilePos.getRow(), tilePos.getCol()-1)].setWalls(false, false, false, true);
  185. break;
  186. case Direction.RIGHT:
  187. tiles[tileId].setWalls(false, false, false, true);
  188. tiles[Position.toID(tilePos.getRow(), tilePos.getCol()+1)].setWalls(false, false, true, false);
  189. break;
  190. }
  191. }
  192. return 0;
  193. }
  194. private String getTileBody (int row, int col, int theseusTile, int minotaurTile) {
  195. int tileId = Position.toID(row, col);
  196. boolean T = (tileId == theseusTile) ? true : false;
  197. boolean M = (tileId == minotaurTile) ? true : false;
  198. int S = tiles[tileId].hasSupply(supplies);
  199. if (T && !M) return " T ";
  200. else if (M && !T) return " M ";
  201. else if (T && M) return "T+M";
  202. else if (S != Const.noSupply)
  203. return String.format("s%02d", S+1);
  204. else return " ";
  205. }
  206. private void renderTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile) {
  207. IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
  208. int tileId = Position.toID(row, col);
  209. frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---" : "+ ";
  210. frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
  211. + getTileBody(row, col, theseusTile, minotaurTile);
  212. frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---" : "+ ";
  213. }
  214. private void renderSentinelTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile ) {
  215. IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
  216. int tileId = Position.toID(row, col);
  217. frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---+" : "+ +";
  218. frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
  219. + getTileBody(row, col, theseusTile, minotaurTile)
  220. + (tiles[tileId].hasWall(Direction.RIGHT)? "|" : " ");
  221. frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---+" : "+ +";
  222. }
  223. /** @name Class data */
  224. /** @{ */
  225. private int N; /**< The size of each edge of the board */
  226. private int S; /**< The number of the supplies on the board */
  227. private int W; /**< The number of walls on the board */
  228. private Tile[] tiles; /**< Array to hold all the tiles for the board */
  229. private Supply[] supplies; /**< Array to hold all the supplies on the board */
  230. /** @} */
  231. }