/** * @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=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". * see also here * @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". * see also here * @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= 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 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 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 */ /** @} */ }