|
- /**
- * @file Board.java
- *
- * @author Christos Choutouridis AEM:8997
- * @email cchoutou@ece.auth.gr
- */
-
- package net.hoo2.auth.labyrinth;
-
- import java.util.function.IntFunction;
-
- /**
- * @brief
- * This class is the representation of the games's board
- *
- * The board is the square arrangement of the tiles. This class is also
- * the owner of the tile and supply objects.
- */
- public class Board {
- /** @name Constructors */
- /** @{ */
-
- /**
- * The empty constructor for default initialization
- */
- protected Board() {
- this.N = this.S = this.W = 0;
- this.tiles = null;
- this.supplies =null;
- }
-
- /**
- * The main constructor for the application
- * @param N The size of each edge of the board
- * @param S The number of supplies on the board
- * @param W The number of walls on the board
- */
- protected Board(int N, int S, int W) {
- assert (W>= 4*N-1 && W<=(3*N*N+1)/2 )
- : "Boards walls has to be in the range [4N-1, (3N^2+1)/2]";
-
- this.N = Session.boardSize = N;
- this.S = S;
- this.W = W;
- tiles = new Tile[N*N];
- supplies = new Supply[S];
- }
-
- /**
- * Deep copy constructor
- * @param b The board to copy
- *
- * @note
- * The lack of value semantics in java is (in author's opinion) one of the greatest
- * weakness of the language and one of the reasons why it will never be a language
- * to care about. To quote Alexander Stepanof's words in "elements of programming" section 1.5:
- * "Assignment is a procedure that takes two objects of the same type and makes the first
- * object equal to the second without modifying the second".
- * In this class we try to cope with this situation knowing that we can not do anything about
- * assignment operator. We just add value semantics to the copy constructor and go on with our lifes...
- */
- protected Board(Board b) {
- // Copy primitives
- this.N = b.N;
- this.S = b.S;
- this.W = b.W;
- // Clone arrays
- this.tiles = b.tiles.clone();
- this.supplies = b.supplies.clone();
- }
- /** @} */
-
- /** @name Supply's main application interface */
- /** @{ */
-
- protected void createTiles() {
- int wallPool = W;
- wallPool -= createBasicTileWalls ();
- createInnerWalls(wallPool);
- }
-
- protected void createSupplies(int theseusTile, int minotaurTile) {
- ShuffledRange rand = new ShuffledRange(0, N*N);
- for (int tileId, i=0 ; i<supplies.length ; ++i) {
- do
- tileId = rand.get();
- while (tileId == theseusTile || tileId == minotaurTile);
- supplies[i] = new Supply(i, tileId);
- }
- }
-
- protected void createBoard(int theseusTile, int minotaurTile) {
- createTiles();
- createSupplies(theseusTile, minotaurTile);
- }
-
- protected String[][] getStringRepresentation(int theseusTile, int minotaurTile) {
- String[][] frame = new String[2*N+1][N];
-
- for (int row=0 ; row<N ; ++row) {
- int col;
- for (col =0 ; col<N-1 ; ++col)
- renderTile(frame, row, col, theseusTile, minotaurTile);
- renderSentinelTile(frame, row, col, theseusTile, minotaurTile);
- }
- return frame;
- }
-
- protected void printBoard (String[][] sBoard) {
- for (int i=sBoard.length-1 ; i>=0 ; --i) {
- for (String it : sBoard[i])
- System.out.print(it);
- System.out.println();
- }
- }
-
- int size() { return N; }
-
- /** @} */
-
- /**
- * @name Accessor/Mutator interface
- * @note
- * Please consider not to use mutator interface. Its the abstraction killer :(
- */
- /** @{ */
- protected int getN() { return N; }
- protected int getS() { return S; }
- protected int getW() { return W; }
- /**
- * @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure".
- * <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a>
- * @return Reference to inner tiles array.
- */
- protected Tile[] getTiles() { return tiles; }
- /**
- * @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure".
- * <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a>
- * @return Reference to inner supplies array.
- */
- protected Supply[] getSupplies() { return supplies; }
-
- protected void setN(int N) { this.N = N; }
- protected void setS(int S) { this.S = S; }
- protected void setW(int W) { this.W = W; }
-
- /**
- * @param tiles Reference to tiles that we want to act as replacement for the inner tiles array.
- * @note Use with care.
- * Any call to this function will probably add memory for the garbage collector.
- */
- protected void setTiles(Tile[] tiles) { this.tiles = tiles; }
- /**
- * @param supplies Reference to supplies that we want to act as replacement for the inner supplies array.
- * @note Use with care.
- * Any call to this function will probably add memory for the garbage collector.
- */
- protected void setSupplies(Supply[] supplies) { this.supplies= supplies; }
- /** @} */
-
- private int createBasicTileWalls () {
- int tileCount =0;
- // Create basic tiles and outer walls
- for (int i =0 ; i< tiles.length ; ++i) {
- int r = Position.toRow(i);
- int c = Position.toCol(i);
- boolean up = (r == N-1) ? true : false;
- boolean down = (r == 0 && c != 0) ? true : false;
- boolean left = (c == 0) ? true : false;
- boolean right = (c == N-1) ? true : false;
- tileCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0));
- tiles[i] = new Tile (i, up, down, left, right);
- }
- return tileCount;
- }
-
- private int createInnerWalls (int wallPool) {
- // Create inner walls for the rest of the desired walls
- ShuffledRange randTiles = new ShuffledRange(0, N*N);
- for (int tileId, i =0 ; i<wallPool ; ++i) {
- // randomly pick a tile with less than 2 walls
- do {
- tileId = randTiles.get();
- if (tileId == Const.noTileId)
- return 0;
- } while (tiles[tileId].hasWalls() >= 2); //XXX: Predicate : can put wall
- // Randomly pick a not used direction in that tile
- ShuffledRange randDirections = new ShuffledRange(Direction.Begin, Direction.Step, Direction.End);
- Position tilePos = new Position(tileId);
- int dir;
- do
- dir = randDirections.get();
- while (tiles[tileId].hasWall(dir));
- switch (dir) {
- case Direction.UP:
- tiles[tileId].setWalls(true, false, false, false);
- tiles[Position.toID(tilePos.getRow()+1, tilePos.getCol())].setWalls(false, true, false, false);
- break;
- case Direction.DOWN:
- tiles[tileId].setWalls(false, true, false, false);
- tiles[Position.toID(tilePos.getRow()-1, tilePos.getCol())].setWalls(true, false, false, false);
- break;
- case Direction.LEFT:
- tiles[tileId].setWalls(false, false, true, false);
- tiles[Position.toID(tilePos.getRow(), tilePos.getCol()-1)].setWalls(false, false, false, true);
- break;
- case Direction.RIGHT:
- tiles[tileId].setWalls(false, false, false, true);
- tiles[Position.toID(tilePos.getRow(), tilePos.getCol()+1)].setWalls(false, false, true, false);
- break;
- }
- }
- return 0;
- }
-
- private String getTileBody (int row, int col, int theseusTile, int minotaurTile) {
- int tileId = Position.toID(row, col);
- boolean T = (tileId == theseusTile) ? true : false;
- boolean M = (tileId == minotaurTile) ? true : false;
- int S = tiles[tileId].hasSupply(supplies);
-
- if (T && !M) return " T ";
- else if (M && !T) return " M ";
- else if (T && M) return "T+M";
- else if (S != Const.noSupply)
- return String.format("s%02d", S+1);
- else return " ";
- }
-
- private void renderTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile) {
- IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
-
- int tileId = Position.toID(row, col);
- frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---" : "+ ";
- frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
- + getTileBody(row, col, theseusTile, minotaurTile);
- frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---" : "+ ";
- }
-
-
- private void renderSentinelTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile ) {
- IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
-
- int tileId = Position.toID(row, col);
- frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---+" : "+ +";
- frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
- + getTileBody(row, col, theseusTile, minotaurTile)
- + (tiles[tileId].hasWall(Direction.RIGHT)? "|" : " ");
- frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---+" : "+ +";
- }
-
- /** @name Class data */
- /** @{ */
- private int N; /**< The size of each edge of the board */
- private int S; /**< The number of the supplies on the board */
- private int W; /**< The number of walls on the board */
- private Tile[] tiles; /**< Array to hold all the tiles for the board */
- private Supply[] supplies; /**< Array to hold all the supplies on the board */
- /** @} */
- }
|