658 lines
24 KiB
Java
658 lines
24 KiB
Java
/**
|
|
* @file Board.java
|
|
*
|
|
* @author
|
|
* Anastasia Foti AEM:8959
|
|
* <anastaskf@ece.auth.gr>
|
|
*
|
|
* @author
|
|
* Christos Choutouridis AEM:8997
|
|
* <cchoutou@ece.auth.gr>
|
|
*/
|
|
|
|
package host.labyrinth;
|
|
|
|
import java.util.ArrayList;
|
|
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.
|
|
*/
|
|
class Board {
|
|
/** @name Constructors */
|
|
/** @{ */
|
|
|
|
/**
|
|
* The empty constructor for default initialization
|
|
*/
|
|
Board() {
|
|
this.N = 0;
|
|
this.S = 0;
|
|
this.W = 0;
|
|
tiles = null;
|
|
supplies = null;
|
|
walls = new ArrayList<Edge>();
|
|
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE];
|
|
playerCount =0;
|
|
}
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
Board(int N, int S) {
|
|
assert (N%2 != 0) : "Board's size has to be an odd number.";
|
|
assert (S <= (N*N-2)) : "At least 2 tiles has to be without supplies.";
|
|
this.N = Session.boardSize = N;
|
|
this.S = S;
|
|
this.W = 0;
|
|
tiles = new Tile[N*N];
|
|
supplies = new Supply[S];
|
|
walls = new ArrayList<Edge>();
|
|
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE];
|
|
playerCount =0;
|
|
}
|
|
|
|
/**
|
|
* 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...
|
|
*/
|
|
Board(Board b) {
|
|
// Copy primitives
|
|
this.N = b.N;
|
|
this.S = b.S;
|
|
this.W = b.W;
|
|
tiles = new Tile[b.tiles.length];
|
|
supplies = new Supply[b.supplies.length];
|
|
walls = new ArrayList<Edge>();
|
|
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE];
|
|
playerCount =b.playerCount;
|
|
|
|
// clone moves array of array of primitives
|
|
for (int i=0 ; i<b.moves.length ; ++i)
|
|
this.moves[i] = b.moves[i].clone();
|
|
|
|
// Clone arrays of objects
|
|
for (int i=0 ; i<b.tiles.length ; ++i)
|
|
this.tiles[i] = new Tile(b.tiles[i]);
|
|
for (int i=0 ; i<b.supplies.length ; ++i)
|
|
this.supplies[i] = new Supply(b.supplies[i]);
|
|
|
|
// clone vectors
|
|
for (Edge it: b.walls)
|
|
this.walls.add(new Edge(it));
|
|
}
|
|
/** @} */
|
|
|
|
/** @name Board's main application interface */
|
|
/** @{ */
|
|
|
|
/**
|
|
* Creates the board with all the requested walls and supplies.
|
|
*
|
|
* @param theseusTile
|
|
* @param minotaurTile
|
|
*/
|
|
void createBoard(int theseusTile, int minotaurTile) {
|
|
createTiles();
|
|
createSupplies(theseusTile, minotaurTile);
|
|
}
|
|
|
|
/**
|
|
* Returns a 2-D array with the string representation of the board.
|
|
*
|
|
* The rows of the array represent the Y-coordinate and the columns the X-coordinate.
|
|
* The only difference is that between each row there is an extra row with the possible
|
|
* walls. This way the number of rows of the returning array are 2N+1 and the number of
|
|
* columns N+1.\n
|
|
* So each tile of the board is represented by 3 strings. One for the north wall, one for
|
|
* the body and one for the south wall.
|
|
*
|
|
* @param theseusTile The current Theseus tile
|
|
* @param minotaurTile The current Minotaur tile
|
|
* @return The string representation of the board
|
|
*/
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Print board utility.
|
|
* @param sBoard Reference to string representation of the board to print.
|
|
*
|
|
* @note
|
|
* As the lower row addresses of the string representation of the board contain
|
|
* the south rows, in order to view the board correctly we have to print the rows
|
|
* in the opposite order.
|
|
*/
|
|
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();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Predicate to check if a direction is Walkable.
|
|
*
|
|
* A `walkable` direction is a tile direction where:
|
|
* <ul>
|
|
* <li>The wall is not the DOWN wall from tile (0, 0).
|
|
* <li>There is not already a wall in the desired direction. (Implies no sentinel tile).
|
|
* </ul>
|
|
*
|
|
* @param tileId The starting tileId.
|
|
* @param direction The desired direction.
|
|
* @return True if it is walkable.
|
|
*/
|
|
boolean isWalkable(int tileId, int direction) {
|
|
return !tiles[tileId].hasWall(direction)
|
|
&& !(tileId == 0 && direction == Direction.DOWN);
|
|
}
|
|
|
|
/**
|
|
* Predicate to check if a direction is Walkable.
|
|
*
|
|
* A `walkable` direction is a tile direction where:
|
|
* <ul>
|
|
* <li>The wall is not the DOWN wall from tile (0, 0).
|
|
* <li>There is not already a wall in the desired direction. (Implies no sentinel tile).
|
|
* </ul>
|
|
*
|
|
* @param row Row position of the starting tile.
|
|
* @param col Column position of the starting tile.
|
|
* @param direction The desired direction.
|
|
* @return True if it is walkable.
|
|
*/
|
|
boolean isWalkable(int row, int col, int direction) {
|
|
return !tiles[Position.toID(row, col)].hasWall(direction)
|
|
&& !(Position.toID(row, col) == 0 && direction == Direction.DOWN);
|
|
}
|
|
|
|
/**
|
|
* Utility function to check if there is a supply on the tile or not
|
|
* @param tileId The tile to check
|
|
* @return Yes/no
|
|
*/
|
|
boolean hasSupply (int tileId) {
|
|
return (Const.noSupply != tiles[tileId].hasSupply(supplies)) ? true : false;
|
|
}
|
|
|
|
/**
|
|
* Try to pick supply from a tile. If succeed it also erases the
|
|
* supply from the board.
|
|
*
|
|
* @param tileId The tile to check
|
|
* @return The id of supply.
|
|
* @arg Const.noSupply if there is none
|
|
* @arg The ID of supply if there is one.
|
|
*/
|
|
int tryPickSupply(int tileId) {
|
|
int supplyId = tiles[tileId].hasSupply(supplies);
|
|
if (supplyId != Const.noSupply) {
|
|
tiles[tileId].pickSupply(supplies, supplyId);
|
|
}
|
|
return supplyId;
|
|
}
|
|
|
|
|
|
/**
|
|
* A plain fair dice functionality provided by the board.
|
|
* @return A random direction;
|
|
*/
|
|
int dice () {
|
|
ShuffledRange d = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step);
|
|
return d.get();
|
|
}
|
|
|
|
/** @return the size of each site of the board. */
|
|
int size () { return N; }
|
|
|
|
/**
|
|
* Utility function to create player IDs
|
|
* @return The generated player id.
|
|
*/
|
|
int generatePlayerId () throws Exception {
|
|
if (playerCount < Const.numOfPlayers)
|
|
return playerCount++;
|
|
else
|
|
throw new Exception("Maximum number of players exceeded");
|
|
}
|
|
|
|
/**
|
|
* Boards utility to give access to other player Id.
|
|
*
|
|
* @param playerId The id of player who asks
|
|
* @return The other player's Id.
|
|
*/
|
|
int getOpponentId(int playerId) {
|
|
return Const.numOfPlayers - (playerId +1);
|
|
}
|
|
|
|
/**
|
|
* Boards utility to give access to other player moves.
|
|
*
|
|
* @param playerId The id of player who asks
|
|
* @return The moves data of other player
|
|
*/
|
|
int[] getOpponentMove (int playerId) {
|
|
return moves[getOpponentId(playerId)];
|
|
}
|
|
|
|
/**
|
|
* Utility to update the moves of each player.
|
|
*
|
|
* This function is used by the players to update their position on the board.
|
|
* After that a player can read other player positions using getOpponentMoves()
|
|
* @see getOpponentMoves()
|
|
*
|
|
* @param m Reference to new move data
|
|
* @param playerId The id of the player who update his/her data.
|
|
*/
|
|
void updateMove(int[] m, int playerId) {
|
|
//moves.set(playerId, Arrays.stream(m).boxed().toArray(Integer[]::new));
|
|
moves[playerId] = m;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/**
|
|
* @name Accessor/Mutator interface
|
|
* @note
|
|
* Please consider not to use mutator interface. Its the abstraction killer :(
|
|
*/
|
|
/** @{ */
|
|
int getN() { return N; }
|
|
int getS() { return S; }
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
Supply[] getSupplies() { return supplies; }
|
|
|
|
/**
|
|
* @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 walls array.
|
|
*/
|
|
ArrayList<Edge> getWalls() { return walls; }
|
|
|
|
/**
|
|
* @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 walls array.
|
|
*/
|
|
int[][] getMoves() { return moves; }
|
|
|
|
void setN(int N) { this.N = N; }
|
|
void setS(int S) { this.S = S; }
|
|
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.
|
|
*/
|
|
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.
|
|
*/
|
|
void setSupplies(Supply[] supplies) { this.supplies= supplies; }
|
|
|
|
/**
|
|
* @param walls Reference to walls that we want to act as replacement for the inner walls vector.
|
|
* @note Use with care.
|
|
* Any call to this function will probably add memory for the garbage collector.
|
|
*/
|
|
void setWalls (ArrayList<Edge> walls) { this.walls= walls; }
|
|
|
|
/**
|
|
* @param moves Reference to moves that we want to act as replacement for the inner moves vector.
|
|
* @note Use with care.
|
|
* Any call to this function will probably add memory for the garbage collector.
|
|
*/
|
|
void setMoves(int[][] moves) { this.moves =moves; }
|
|
|
|
/** @} */
|
|
|
|
|
|
/** @name Sentinel predicates */
|
|
/** @{ */
|
|
private boolean isLeftSentinel (int tileId) { return (Position.toCol(tileId) == 0); }
|
|
private boolean isRightSentinel (int tileId) { return (Position.toCol(tileId) == N-1); }
|
|
private boolean isUpSentinel (int tileId) { return (Position.toRow(tileId) == N-1); }
|
|
private boolean isDownSentinel (int tileId) { return (Position.toRow(tileId) == 0); }
|
|
/** @} */
|
|
|
|
/**
|
|
* @name private functionality of the object
|
|
*/
|
|
/** @{ */
|
|
|
|
/**
|
|
* This function creates randomly all the tiles of the board
|
|
*/
|
|
private void createTiles() {
|
|
int wallCount;
|
|
wallCount = createBasicTileWalls (); // First create tiles with outer walls
|
|
wallCount += createInnerWalls(); // Greedy create as many inner walls we can
|
|
W = wallCount;
|
|
}
|
|
|
|
/**
|
|
* This function create randomly the board's supplies.
|
|
*
|
|
* The supplies has to be in separate tiles and in tiles with no player
|
|
*
|
|
* @param theseusTile The tile of the Theseus
|
|
* @param minotaurTile The tile of the Minotaur
|
|
*/
|
|
private void createSupplies(int theseusTile, int minotaurTile) {
|
|
ShuffledRange rand = new ShuffledRange(0, N*N); // Make a shuffled range of all tiles
|
|
for (int tileId, i=0 ; i<supplies.length ; ++i) {
|
|
// Pick a tile as long as there is no player in it
|
|
do
|
|
tileId = rand.get();
|
|
while (tileId == theseusTile || tileId == minotaurTile);
|
|
supplies[i] = new Supply(i, tileId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Predicate to check if a wall creates a closed room.
|
|
*
|
|
* This algorithm has a complexity of @f$ O(N^2logN) @f$ where N represents the total
|
|
* number of tiles.
|
|
* It should be used with care.
|
|
*
|
|
* @param tileId The tileId of the wall.
|
|
* @param direction The wall's relative direction.
|
|
* @return True if the wall creates a closed room, false otherwise.
|
|
*/
|
|
private boolean isRoomCreator (int tileId, int direction) {
|
|
// Clone the list of all the walls locally.
|
|
ArrayList<Edge> w = new ArrayList<Edge>();
|
|
for (Edge it : walls)
|
|
w.add(new Edge(it));
|
|
// Create the largest possible coherent graph from the list of walls(edges)
|
|
Graph g = new Graph(new Edge(tileId, direction));
|
|
int size;
|
|
do {
|
|
size = w.size(); // mark the size (before the pass)
|
|
for (int i =0, S=w.size() ; i<S ; ++i) // for each edge(wall) on the local wall list
|
|
if (g.attach(w.get(i))) { // can we attach the edge(wall) to the graph ?
|
|
w.remove(i); // if yes remove it from the local wall list
|
|
--i; --S; // decrease iterator and size to match ArrayList's new values
|
|
}
|
|
} while (size != w.size()); // If the size hasn't change(no new graph leafs) exit
|
|
|
|
// Search if a vertex is attached to the graph more than once.
|
|
// This means that there is at least 2 links to the same node
|
|
// so the graph has a closed loop
|
|
for (Edge it : walls) {
|
|
if (g.count(it.getV1()) > 1) return true;
|
|
if (g.count(it.getV2()) > 1) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Predicate to check if a tile direction is `Wallable`.
|
|
*
|
|
* A `wallable` direction is a tile direction where:
|
|
* <ul>
|
|
* <li>The wall is not the DOWN wall from tile (0, 0).
|
|
* <li>There is not already a wall in the desired direction. (Implies no sentinel tile).
|
|
* <li>The neighbor in this direction has at most `Const.maxTileWalls -1` walls.
|
|
* <li>The wall does not create a closed room (Optional requirement).
|
|
* </ul>
|
|
*
|
|
* @note
|
|
* A wallable direction automatically implies that the direction in not an outer wall.
|
|
*
|
|
* @param tileId The tile to check.
|
|
* @param direction The direction to check
|
|
* @return True if the direction is wallable.
|
|
*/
|
|
private boolean isWallableDir (int tileId, int direction) {
|
|
// Check list
|
|
if (!isWalkable(tileId, direction))
|
|
return false;
|
|
switch (direction) {
|
|
case Direction.UP:
|
|
if (tiles[upTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
|
|
break;
|
|
case Direction.DOWN:
|
|
if (tiles[downTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
|
|
break;
|
|
case Direction.LEFT:
|
|
if (tiles[leftTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
|
|
break;
|
|
case Direction.RIGHT:
|
|
if (tiles[rightTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
|
|
break;
|
|
}
|
|
if (Session.loopGuard && isRoomCreator(tileId, direction))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Predicate to check if a tile is `Wallable`.
|
|
*
|
|
* A `wallable` tile is a tile where:
|
|
* <ul>
|
|
* <li>The tile has at most `Const.maxTileWalls -1` walls.
|
|
* <li>There is at least one wallable direction on the tile.
|
|
* </ul>
|
|
* @param tileId The tile to check
|
|
* @return True if the tile is wallable.
|
|
*/
|
|
private boolean isWallable (int tileId) {
|
|
// Check list
|
|
if (tileId == Const.noTileId)
|
|
return false;
|
|
if (tiles[tileId].hasWalls() >= Const.maxTileWalls)
|
|
return false;
|
|
Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
|
|
for (int dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get())
|
|
if (isWallableDir(tileId, dir))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* This utility function create/allocate the tiles of the board and create
|
|
* the outer walls at the same time.
|
|
*
|
|
* @return The number of walls created from the utility.
|
|
*/
|
|
private int createBasicTileWalls () {
|
|
int wallCount =0;
|
|
for (int i =0 ; i< tiles.length ; ++i) {
|
|
boolean up = isUpSentinel(i);
|
|
boolean down = isDownSentinel(i) && (i != 0);
|
|
boolean left = isLeftSentinel(i);
|
|
boolean right = isRightSentinel(i);
|
|
wallCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0));
|
|
tiles[i] = new Tile (i, up, down, left, right);
|
|
// If we have loopGuard enable we populate walls also.
|
|
if (Session.loopGuard) {
|
|
if (up) walls.add(new Edge(i, Direction.UP));
|
|
if (down) walls.add(new Edge(i, Direction.DOWN));
|
|
if (left) walls.add(new Edge(i, Direction.LEFT));
|
|
if (right) walls.add(new Edge(i, Direction.RIGHT));
|
|
}
|
|
}
|
|
return wallCount;
|
|
}
|
|
|
|
/**
|
|
* Create randomly a wall in the wallable selected tile.
|
|
* @param tileId The wallable tile to create the wall
|
|
*/
|
|
private void createInnerWall(int tileId) {
|
|
// Randomly pick a wallable direction in that tile.
|
|
ShuffledRange randDirections = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step);
|
|
int dir;
|
|
do
|
|
dir = randDirections.get();
|
|
while (!isWallableDir(tileId, dir));
|
|
// Add wall to tileId and the adjacent tileId
|
|
Position neighbor = new Position(Position.toRow(tileId), Position.toCol(tileId), dir);
|
|
tiles[tileId].setWall(dir);
|
|
tiles[neighbor.getId()].setWall(Direction.opposite(dir));
|
|
// If we have loopGuard enable we populate walls also.
|
|
if (Session.loopGuard)
|
|
walls.add(new Edge(tileId, dir));
|
|
}
|
|
|
|
/**
|
|
* This utility creates the inner walls of the board.
|
|
*
|
|
* @return The number of walls failed to create.
|
|
*/
|
|
private int createInnerWalls () {
|
|
ShuffledRange randTiles = new ShuffledRange(0, N*N);
|
|
for (int tileId, walls =0, shuffleMark =0 ; true ; ) {
|
|
// randomly pick a wallable tile.
|
|
do {
|
|
if ((tileId = randTiles.get())== Const.EOR) {
|
|
if (walls == shuffleMark) // Wallable tiles exhausted.
|
|
return walls;
|
|
else { // Re-shuffle and continue.
|
|
randTiles = new ShuffledRange(0, N*N);
|
|
shuffleMark =walls;
|
|
}
|
|
}
|
|
} while (!isWallable(tileId));
|
|
++walls;
|
|
createInnerWall(tileId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Utility to get the body (center line) of the string representation of the tile.
|
|
*
|
|
* @param row What board's row to get.
|
|
* @param col What board's column to get.
|
|
* @param theseusTile The current tile of the Theseus.
|
|
* @param minotaurTile The current tile of the Minotaur.
|
|
* @return The body string
|
|
*/
|
|
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 (T && M) return "T+M";
|
|
else if (M) {
|
|
if (S == Const.noSupply) return " M ";
|
|
else return "M+s";
|
|
}
|
|
else if (S != Const.noSupply)
|
|
return String.format("s%02d", S+1);
|
|
else return " ";
|
|
}
|
|
|
|
/**
|
|
* Utility to render the 3 strings of the tile in the representation frame.
|
|
*
|
|
* @param frame Reference to the frame to print into.
|
|
* @param row The board's row to print.
|
|
* @param col The board's column to print.
|
|
* @param theseusTile The current tile of the Theseus.
|
|
* @param minotaurTile The current tile of the Minotaur.
|
|
*/
|
|
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) ? "+---" : "+ ";
|
|
}
|
|
|
|
/**
|
|
* Utility to render the 3 strings of the tile in the representation frame in
|
|
* the case the tile lies in the east wall. We call these tiles `sentinel tiles`
|
|
*
|
|
* @param frame Reference to the frame to print into.
|
|
* @param row The board's row to print.
|
|
* @param col The board's column to print.
|
|
* @param theseusTile The current tile of the Theseus.
|
|
* @param minotaurTile The current tile of the Minotaur.
|
|
*/
|
|
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 Neighbor access lambdas */
|
|
/** @{ */
|
|
private IntFunction<Integer> leftTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)-1); };
|
|
private IntFunction<Integer> rightTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)+1); };
|
|
private IntFunction<Integer> upTileId = (id) -> { return Position.toID(Position.toRow(id)+1, Position.toCol(id) ); };
|
|
private IntFunction<Integer> downTileId = (id) -> { return Position.toID(Position.toRow(id)-1, Position.toCol(id) ); };
|
|
/** @} */
|
|
|
|
/** @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 */
|
|
private ArrayList<Edge> walls; /**<
|
|
* Array to hold all the walls using the edge representation
|
|
* required by the closed room preventing algorithm.
|
|
*/
|
|
private int[][] moves;
|
|
private int playerCount;
|
|
/** @} */
|
|
}
|