Labyrinth
A labyrinth game assignment
Board.java
Go to the documentation of this file.
1 
10 package host.labyrinth;
11 
12 import java.util.ArrayList;
13 import java.util.function.IntFunction;
14 
22 class Board {
29  Board() {
30  this.N = 0;
31  this.S = 0;
32  this.W = 0;
33  tiles = null;
34  supplies =null;
35  walls = new ArrayList<Edge>();
36  }
37 
43  Board(int N, int S) {
44  assert (N%2 != 0) : "Board's size has to be an odd number.";
45  assert (S <= (N*N-2)) : "At least 2 tiles has to be without supplies.";
46  this.N = Session.boardSize = N;
47  this.S = S;
48  this.W = 0;
49  tiles = new Tile[N*N];
50  supplies = new Supply[S];
51  walls = new ArrayList<Edge>();
52  }
53 
67  Board(Board b) {
68  // Copy primitives
69  this.N = b.N;
70  this.S = b.S;
71  this.W = b.W;
72  // Clone arrays
73  this.tiles = b.tiles.clone();
74  this.supplies = b.supplies.clone();
75  this.walls = b.walls;
76  }
88  void createBoard(int theseusTile, int minotaurTile) {
89  createTiles();
90  createSupplies(theseusTile, minotaurTile);
91  }
92 
107  String[][] getStringRepresentation(int theseusTile, int minotaurTile) {
108  String[][] frame = new String[2*N+1][N];
109 
110  for (int row=0 ; row<N ; ++row) {
111  int col;
112  for (col =0 ; col<N-1 ; ++col)
113  renderTile(frame, row, col, theseusTile, minotaurTile);
114  renderSentinelTile(frame, row, col, theseusTile, minotaurTile);
115  }
116  return frame;
117  }
118 
128  void printBoard (String[][] sBoard) {
129  for (int i=sBoard.length-1 ; i>=0 ; --i) {
130  for (String it : sBoard[i])
131  System.out.print(it);
132  System.out.println();
133  }
134  }
135 
149  boolean isWalkable(int tileId, int direction) {
150  return !tiles[tileId].hasWall(direction)
151  && !(tileId == 0 && direction == Direction.DOWN);
152  }
153 
168  boolean isWalkable(int row, int col, int direction) {
169  return !tiles[Position.toID(row, col)].hasWall(direction)
170  && !(Position.toID(row, col) == 0 && direction == Direction.DOWN);
171  }
172 
182  int tryPickSupply(int tileId) {
183  int supplyId = tiles[tileId].hasSupply(supplies);
184  if (supplyId != Const.noSupply) {
185  tiles[tileId].pickSupply(supplies, supplyId);
186  }
187  return supplyId;
188  }
189 
194  int dice () {
196  return d.get();
197  }
198 
200  int size () { return N; }
209  int getN() { return N; }
210  int getS() { return S; }
211  int getW() { return W; }
217  Tile[] getTiles() { return tiles; }
223  Supply[] getSupplies() { return supplies; }
224 
230  ArrayList<Edge> getWalls() { return walls; }
231 
232  void setN(int N) { this.N = N; }
233  void setS(int S) { this.S = S; }
234  void setW(int W) { this.W = W; }
235 
241  void setTiles(Tile[] tiles) { this.tiles = tiles; }
247  void setSupplies(Supply[] supplies) { this.supplies= supplies; }
248 
254  void setWalls (ArrayList<Edge> walls) { this.walls= walls; }
255 
261  private boolean isLeftSentinel (int tileId) { return (Position.toCol(tileId) == 0); }
262  private boolean isRightSentinel (int tileId) { return (Position.toCol(tileId) == N-1); }
263  private boolean isUpSentinel (int tileId) { return (Position.toRow(tileId) == N-1); }
264  private boolean isDownSentinel (int tileId) { return (Position.toRow(tileId) == 0); }
275  private void createTiles() {
276  int wallCount;
277  wallCount = createBasicTileWalls (); // First create tiles with outer walls
278  wallCount += createInnerWalls(); // Greedy create as many inner walls we can
279  W = wallCount;
280  }
281 
290  private void createSupplies(int theseusTile, int minotaurTile) {
291  ShuffledRange rand = new ShuffledRange(0, N*N); // Make a shuffled range of all tiles
292  for (int tileId, i=0 ; i<supplies.length ; ++i) {
293  // Pick a tile as long as there is no player in it
294  do
295  tileId = rand.get();
296  while (tileId == theseusTile || tileId == minotaurTile);
297  supplies[i] = new Supply(i, tileId);
298  }
299  }
300 
312  private boolean isRoomCreator (int tileId, int direction) {
313  // Clone the list of all the walls locally.
314  ArrayList<Edge> w = new ArrayList<Edge>();
315  for (Edge it : walls)
316  w.add(new Edge(it));
317  // Create the largest possible coherent graph from the list of walls(edges)
318  Graph g = new Graph(new Edge(tileId, direction));
319  int size;
320  do {
321  size = w.size(); // mark the size (before the pass)
322  for (int i =0, S=w.size() ; i<S ; ++i) // for each edge(wall) on the local wall list
323  if (g.attach(w.get(i))) { // can we attach the edge(wall) to the graph ?
324  w.remove(i); // if yes remove it from the local wall list
325  --i; --S; // decrease iterator and size to match ArrayList's new values
326  }
327  } while (size != w.size()); // If the size hasn't change(no new graph leafs) exit
328 
329  // Search if a vertex is attached to the graph more than once.
330  // This means that there is at least 2 links to the same node
331  // so the graph has a closed loop
332  for (Edge it : walls) {
333  if (g.count(it.getV1()) > 1) return true;
334  if (g.count(it.getV2()) > 1) return true;
335  }
336  return false;
337  }
338 
357  private boolean isWallableDir (int tileId, int direction) {
358  // Check list
359  if (!isWalkable(tileId, direction))
360  return false;
361  switch (direction) {
362  case Direction.UP:
363  if (tiles[upTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
364  break;
365  case Direction.DOWN:
366  if (tiles[downTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
367  break;
368  case Direction.LEFT:
369  if (tiles[leftTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
370  break;
371  case Direction.RIGHT:
372  if (tiles[rightTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
373  break;
374  }
375  if (Session.loopGuard && isRoomCreator(tileId, direction))
376  return false;
377  return true;
378  }
379 
391  private boolean isWallable (int tileId) {
392  // Check list
393  if (tileId == Const.noTileId)
394  return false;
395  if (tiles[tileId].hasWalls() >= Const.maxTileWalls)
396  return false;
398  for (int dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get())
399  if (isWallableDir(tileId, dir))
400  return true;
401  return false;
402  }
403 
404 
411  private int createBasicTileWalls () {
412  int wallCount =0;
413  for (int i =0 ; i< tiles.length ; ++i) {
414  boolean up = isUpSentinel(i);
415  boolean down = isDownSentinel(i) && (i != 0);
416  boolean left = isLeftSentinel(i);
417  boolean right = isRightSentinel(i);
418  wallCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0));
419  tiles[i] = new Tile (i, up, down, left, right);
420  // If we have loopGuard enable we populate walls also.
421  if (Session.loopGuard) {
422  if (up) walls.add(new Edge(i, Direction.UP));
423  if (down) walls.add(new Edge(i, Direction.DOWN));
424  if (left) walls.add(new Edge(i, Direction.LEFT));
425  if (right) walls.add(new Edge(i, Direction.RIGHT));
426  }
427  }
428  return wallCount;
429  }
430 
435  private void createInnerWall(int tileId) {
436  // Randomly pick a wallable direction in that tile.
438  int dir;
439  do
440  dir = randDirections.get();
441  while (!isWallableDir(tileId, dir));
442  // Add wall to tileId and the adjacent tileId
443  Position neighbor = new Position(Position.toRow(tileId), Position.toCol(tileId), dir);
444  tiles[tileId].setWall(dir);
445  tiles[neighbor.getId()].setWall(Direction.opposite(dir));
446  // If we have loopGuard enable we populate walls also.
447  if (Session.loopGuard)
448  walls.add(new Edge(tileId, dir));
449  }
450 
456  private int createInnerWalls () {
457  ShuffledRange randTiles = new ShuffledRange(0, N*N);
458  for (int tileId, i =0, walls =0, shuffleMark =0 ; true ; ) {
459  // randomly pick a wallable tile.
460  do {
461  if ((tileId = randTiles.get())== Const.EOR) {
462  if (i == shuffleMark) // Wallable tiles exhausted.
463  return walls;
464  else { // Re-shuffle and continue.
465  randTiles = new ShuffledRange(0, N*N);
466  shuffleMark =i;
467  }
468  }
469  } while (!isWallable(tileId));
470  ++walls;
471  createInnerWall(tileId);
472  }
473  }
474 
484  private String getTileBody (int row, int col, int theseusTile, int minotaurTile) {
485  int tileId = Position.toID(row, col);
486  boolean T = (tileId == theseusTile) ? true : false;
487  boolean M = (tileId == minotaurTile) ? true : false;
488  int S = tiles[tileId].hasSupply(supplies);
489 
490  if (T && !M) return " T ";
491  else if (T && M) return "T+M";
492  else if (M) {
493  if (S == Const.noSupply) return " M ";
494  else return "M+s";
495  }
496  else if (S != Const.noSupply)
497  return String.format("s%02d", S+1);
498  else return " ";
499  }
500 
510  private void renderTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile) {
511  IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
512 
513  int tileId = Position.toID(row, col);
514  frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---" : "+ ";
515  frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
516  + getTileBody(row, col, theseusTile, minotaurTile);
517  frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---" : "+ ";
518  }
519 
530  private void renderSentinelTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile ) {
531  IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
532 
533  int tileId = Position.toID(row, col);
534  frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---+" : "+ +";
535  frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
536  + getTileBody(row, col, theseusTile, minotaurTile)
537  + (tiles[tileId].hasWall(Direction.RIGHT)? "|" : " ");
538  frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---+" : "+ +";
539  }
544  private IntFunction<Integer> leftTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)-1); };
545  private IntFunction<Integer> rightTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)+1); };
546  private IntFunction<Integer> upTileId = (id) -> { return Position.toID(Position.toRow(id)+1, Position.toCol(id) ); };
547  private IntFunction<Integer> downTileId = (id) -> { return Position.toID(Position.toRow(id)-1, Position.toCol(id) ); };
552  private int N;
553  private int S;
554  private int W;
555  private Tile[] tiles;
556  private Supply[] supplies;
557  private ArrayList<Edge> walls;
562 }
Board()
The empty constructor for default initialization.
Definition: Board.java:29
void setW(int W)
Definition: Board.java:234
This class is the representation of the supplies in the game.
Definition: Supply.java:20
boolean isWallable(int tileId)
Predicate to check if a tile is Wallable.
Definition: Board.java:391
IntFunction< Integer > upTileId
Definition: Board.java:546
static final int RIGHT
East direction.
Definition: Common.java:39
int hasWalls()
Checks if the tile has walls and return the number of them.
Definition: Tile.java:180
void createBoard(int theseusTile, int minotaurTile)
Creates the board with all the requested walls and supplies.
Definition: Board.java:88
void renderSentinelTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile)
Utility to render the 3 strings of the tile in the representation frame in the case the tile lies in ...
Definition: Board.java:530
This class is the representation of the games&#39;s board.
Definition: Board.java:22
Class to hold constant values for entire application.
Definition: Common.java:17
void pickSupply(Supply[] supplies, int supplyId)
Utility to find a supply in the supplies array and removes it.
Definition: Tile.java:201
Board(Board b)
Deep copy constructor.
Definition: Board.java:67
void setS(int S)
Definition: Board.java:233
int createBasicTileWalls()
This utility function create/allocate the tiles of the board and create the outer walls at the same t...
Definition: Board.java:411
void setSupplies(Supply[] supplies)
Definition: Board.java:247
Helper C++-like enumerator class to hold direction.
Definition: Common.java:37
void createTiles()
This function creates randomly all the tiles of the board.
Definition: Board.java:275
int W
The number of walls on the board.
Definition: Board.java:554
static final int End
Iterator style end of range direction (one place after the last)
Definition: Common.java:65
boolean hasWall(int direction)
Checks if the tile has wall in the requested direction.
Definition: Tile.java:166
static final int Step
Step for iterator style direction.
Definition: Common.java:66
String [][] getStringRepresentation(int theseusTile, int minotaurTile)
Returns a 2-D array with the string representation of the board.
Definition: Board.java:107
Tile [] tiles
Array to hold all the tiles for the board.
Definition: Board.java:555
IntFunction< Integer > rightTileId
Definition: Board.java:545
ArrayList< Edge > walls
Array to hold all the walls using the edge representation required by the closed room preventing algo...
Definition: Board.java:557
String getTileBody(int row, int col, int theseusTile, int minotaurTile)
Utility to get the body (center line) of the string representation of the tile.
Definition: Board.java:484
int createInnerWalls()
This utility creates the inner walls of the board.
Definition: Board.java:456
Tile [] getTiles()
Definition: Board.java:217
This class is the representation of the board&#39;s tile.
Definition: Tile.java:19
Provides a graph functionality for the room preventing algorithm.
Definition: Common.java:327
Helper C++ like enumerator class for direction ranged loops.
Definition: Common.java:63
Board(int N, int S)
The main constructor for the application.
Definition: Board.java:43
int getId()
Read access to id coordinate.
Definition: Common.java:119
int S
The number of the supplies on the board.
Definition: Board.java:553
int N
The size of each edge of the board.
Definition: Board.java:552
static int toRow(int id)
Takes Id coordinate and return the corresponding row coordinate.
Definition: Common.java:139
Supply [] supplies
Array to hold all the supplies on the board.
Definition: Board.java:556
void renderTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile)
Utility to render the 3 strings of the tile in the representation frame.
Definition: Board.java:510
boolean isRoomCreator(int tileId, int direction)
Predicate to check if a wall creates a closed room.
Definition: Board.java:312
boolean isWalkable(int row, int col, int direction)
Predicate to check if a direction is Walkable.
Definition: Board.java:168
ArrayList< Edge > getWalls()
Definition: Board.java:230
static final int EOR
Number to indicate the End Of Range.
Definition: Common.java:21
boolean isLeftSentinel(int tileId)
Definition: Board.java:261
static int toCol(int id)
Takes Id coordinate and return the corresponding column coordinate.
Definition: Common.java:147
IntFunction< Integer > leftTileId
Definition: Board.java:544
An Application wide board position implementation holding just the id coordinate. ...
Definition: Common.java:78
int hasSupply(Supply[] supplies)
Utility to check if the tile has a supply.
Definition: Tile.java:189
static final int maxTileWalls
Number of maximum walls for each tile on the board.
Definition: Common.java:18
static boolean loopGuard
When true a wall creation guard is added to prevent closed rooms inside the board.
Definition: Common.java:30
Supply [] getSupplies()
Definition: Board.java:223
boolean attach(Edge e)
Attach an edge into a graph IFF the graph already has a vertex with the same value as one of the vert...
Definition: Common.java:359
boolean isUpSentinel(int tileId)
Definition: Board.java:263
void setWalls(ArrayList< Edge > walls)
Definition: Board.java:254
boolean isWallableDir(int tileId, int direction)
Predicate to check if a tile direction is Wallable.
Definition: Board.java:357
void setWall(int direction)
Sets the tile&#39;s wall in the requested direction.
Definition: Tile.java:139
int dice()
A plain fair dice functionality provided by the board.
Definition: Board.java:194
static final int noSupply
Number to indicate the absent of supply.
Definition: Common.java:19
static int toID(int row, int col)
Takes row and column coordinates and return the calculated Id coordinate.
Definition: Common.java:130
void setTiles(Tile[] tiles)
Definition: Board.java:241
Application wide object to hold settings like values for the session.
Definition: Common.java:26
Class to create ranges of numbers.
Definition: Common.java:161
boolean isWalkable(int tileId, int direction)
Predicate to check if a direction is Walkable.
Definition: Board.java:149
static int boardSize
Default board&#39;s size (if no one set it via command line)
Definition: Common.java:27
static final int LEFT
West direction.
Definition: Common.java:41
static final int noTileId
Number to indicate wrong tileId.
Definition: Common.java:20
A utility class used for room prevent algorithm.
Definition: Common.java:264
int count(int v)
Counts the number of vertices on the graph with the value of v
Definition: Common.java:368
static final int UP
North direction.
Definition: Common.java:38
static int opposite(int direction)
Utility to get the opposite direction.
Definition: Common.java:48
static final int DOWN
South direction.
Definition: Common.java:40
void createSupplies(int theseusTile, int minotaurTile)
This function create randomly the board&#39;s supplies.
Definition: Board.java:290
IntFunction< Integer > downTileId
Definition: Board.java:547
void createInnerWall(int tileId)
Create randomly a wall in the wallable selected tile.
Definition: Board.java:435
static final int Begin
Iterator style begin of range direction (starting north)
Definition: Common.java:64
void setN(int N)
Definition: Board.java:232
boolean isRightSentinel(int tileId)
Definition: Board.java:262
void printBoard(String[][] sBoard)
Print board utility.
Definition: Board.java:128
int tryPickSupply(int tileId)
Try to pick supply from a tile.
Definition: Board.java:182
Class to create shuffled ranges of numbers.
Definition: Common.java:209
boolean isDownSentinel(int tileId)
Definition: Board.java:264
int get()
Extract and return the first item from the range.
Definition: Common.java:194