Labyrinth
A labyrinth game assignment
Board.java
Go to the documentation of this file.
1 
13 package host.labyrinth;
14 
15 import java.util.ArrayList;
16 import java.util.Arrays;
17 import java.util.function.IntFunction;
18 
26 class Board {
33  Board() {
34  this.N = 0;
35  this.S = 0;
36  this.W = 0;
37  tiles = null;
38  supplies =null;
39  walls = new ArrayList<Edge>();
40  moves = new ArrayList<Integer[]>();
41  }
42 
48  Board(int N, int S) {
49  assert (N%2 != 0) : "Board's size has to be an odd number.";
50  assert (S <= (N*N-2)) : "At least 2 tiles has to be without supplies.";
51  this.N = Session.boardSize = N;
52  this.S = S;
53  this.W = 0;
54  tiles = new Tile[N*N];
55  supplies = new Supply[S];
56  walls = new ArrayList<Edge>();
57  moves = new ArrayList<Integer[]>();
58  }
59 
73  Board(Board b) {
74  // Copy primitives
75  this.N = b.N;
76  this.S = b.S;
77  this.W = b.W;
78  // Clone arrays
79  this.tiles = b.tiles.clone();
80  this.supplies = b.supplies.clone();
81  for (Edge it: b.walls)
82  this.walls.add(new Edge(it));
83  for (Integer[] m : b.moves) {
84  this.moves.add(m);
85  }
86  }
98  void createBoard(int theseusTile, int minotaurTile) {
99  createTiles();
100  createSupplies(theseusTile, minotaurTile);
101  }
102 
117  String[][] getStringRepresentation(int theseusTile, int minotaurTile) {
118  String[][] frame = new String[2*N+1][N];
119 
120  for (int row=0 ; row<N ; ++row) {
121  int col;
122  for (col =0 ; col<N-1 ; ++col)
123  renderTile(frame, row, col, theseusTile, minotaurTile);
124  renderSentinelTile(frame, row, col, theseusTile, minotaurTile);
125  }
126  return frame;
127  }
128 
138  void printBoard (String[][] sBoard) {
139  for (int i=sBoard.length-1 ; i>=0 ; --i) {
140  for (String it : sBoard[i])
141  System.out.print(it);
142  System.out.println();
143  }
144  }
145 
159  boolean isWalkable(int tileId, int direction) {
160  return !tiles[tileId].hasWall(direction)
161  && !(tileId == 0 && direction == Direction.DOWN);
162  }
163 
178  boolean isWalkable(int row, int col, int direction) {
179  return !tiles[Position.toID(row, col)].hasWall(direction)
180  && !(Position.toID(row, col) == 0 && direction == Direction.DOWN);
181  }
182 
188  boolean hasSupply (int tileId) {
189  return (Const.noSupply != tiles[tileId].hasSupply(supplies)) ? true : false;
190  }
191 
201  int tryPickSupply(int tileId) {
202  int supplyId = tiles[tileId].hasSupply(supplies);
203  if (supplyId != Const.noSupply) {
204  tiles[tileId].pickSupply(supplies, supplyId);
205  }
206  return supplyId;
207  }
208 
209 
214  int dice () {
216  return d.get();
217  }
218 
220  int size () { return N; }
221 
228  int[][] getOpponentMoves (int playerId) {
229  int[][] ret = new int[moves.size()-1][Const.moveItems];
230  int ii= 0, ri =0;
231  for (Integer[] m : moves) {
232  if (ii != playerId)
233  ret[ri++] = Arrays.stream(m).mapToInt(i->i).toArray();
234  ++ii;
235  }
236  return ret;
237  }
238 
244  moves.add(null);
245  return moves.size() -1;
246  }
247 
258  void updateMove(int[] m, int playerId) {
259  moves.set(playerId, Arrays.stream(m).boxed().toArray(Integer[]::new));
260  }
261 
270  int getN() { return N; }
271  int getS() { return S; }
272  int getW() { return W; }
278  Tile[] getTiles() { return tiles; }
284  Supply[] getSupplies() { return supplies; }
285 
291  ArrayList<Edge> getWalls() { return walls; }
292 
298  ArrayList<Integer[]> getMoves() { return moves; }
299 
300  void setN(int N) { this.N = N; }
301  void setS(int S) { this.S = S; }
302  void setW(int W) { this.W = W; }
303 
309  void setTiles(Tile[] tiles) { this.tiles = tiles; }
315  void setSupplies(Supply[] supplies) { this.supplies= supplies; }
316 
322  void setWalls (ArrayList<Edge> walls) { this.walls= walls; }
323 
329  void setMoves(ArrayList<Integer[]> moves) { this.moves =moves; }
330 
336  private boolean isLeftSentinel (int tileId) { return (Position.toCol(tileId) == 0); }
337  private boolean isRightSentinel (int tileId) { return (Position.toCol(tileId) == N-1); }
338  private boolean isUpSentinel (int tileId) { return (Position.toRow(tileId) == N-1); }
339  private boolean isDownSentinel (int tileId) { return (Position.toRow(tileId) == 0); }
350  private void createTiles() {
351  int wallCount;
352  wallCount = createBasicTileWalls (); // First create tiles with outer walls
353  wallCount += createInnerWalls(); // Greedy create as many inner walls we can
354  W = wallCount;
355  }
356 
365  private void createSupplies(int theseusTile, int minotaurTile) {
366  ShuffledRange rand = new ShuffledRange(0, N*N); // Make a shuffled range of all tiles
367  for (int tileId, i=0 ; i<supplies.length ; ++i) {
368  // Pick a tile as long as there is no player in it
369  do
370  tileId = rand.get();
371  while (tileId == theseusTile || tileId == minotaurTile);
372  supplies[i] = new Supply(i, tileId);
373  }
374  }
375 
387  private boolean isRoomCreator (int tileId, int direction) {
388  // Clone the list of all the walls locally.
389  ArrayList<Edge> w = new ArrayList<Edge>();
390  for (Edge it : walls)
391  w.add(new Edge(it));
392  // Create the largest possible coherent graph from the list of walls(edges)
393  Graph g = new Graph(new Edge(tileId, direction));
394  int size;
395  do {
396  size = w.size(); // mark the size (before the pass)
397  for (int i =0, S=w.size() ; i<S ; ++i) // for each edge(wall) on the local wall list
398  if (g.attach(w.get(i))) { // can we attach the edge(wall) to the graph ?
399  w.remove(i); // if yes remove it from the local wall list
400  --i; --S; // decrease iterator and size to match ArrayList's new values
401  }
402  } while (size != w.size()); // If the size hasn't change(no new graph leafs) exit
403 
404  // Search if a vertex is attached to the graph more than once.
405  // This means that there is at least 2 links to the same node
406  // so the graph has a closed loop
407  for (Edge it : walls) {
408  if (g.count(it.getV1()) > 1) return true;
409  if (g.count(it.getV2()) > 1) return true;
410  }
411  return false;
412  }
413 
432  private boolean isWallableDir (int tileId, int direction) {
433  // Check list
434  if (!isWalkable(tileId, direction))
435  return false;
436  switch (direction) {
437  case Direction.UP:
438  if (tiles[upTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
439  break;
440  case Direction.DOWN:
441  if (tiles[downTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
442  break;
443  case Direction.LEFT:
444  if (tiles[leftTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
445  break;
446  case Direction.RIGHT:
447  if (tiles[rightTileId.apply(tileId)].hasWalls() >= Const.maxTileWalls) return false;
448  break;
449  }
450  if (Session.loopGuard && isRoomCreator(tileId, direction))
451  return false;
452  return true;
453  }
454 
466  private boolean isWallable (int tileId) {
467  // Check list
468  if (tileId == Const.noTileId)
469  return false;
470  if (tiles[tileId].hasWalls() >= Const.maxTileWalls)
471  return false;
473  for (int dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get())
474  if (isWallableDir(tileId, dir))
475  return true;
476  return false;
477  }
478 
479 
486  private int createBasicTileWalls () {
487  int wallCount =0;
488  for (int i =0 ; i< tiles.length ; ++i) {
489  boolean up = isUpSentinel(i);
490  boolean down = isDownSentinel(i) && (i != 0);
491  boolean left = isLeftSentinel(i);
492  boolean right = isRightSentinel(i);
493  wallCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0));
494  tiles[i] = new Tile (i, up, down, left, right);
495  // If we have loopGuard enable we populate walls also.
496  if (Session.loopGuard) {
497  if (up) walls.add(new Edge(i, Direction.UP));
498  if (down) walls.add(new Edge(i, Direction.DOWN));
499  if (left) walls.add(new Edge(i, Direction.LEFT));
500  if (right) walls.add(new Edge(i, Direction.RIGHT));
501  }
502  }
503  return wallCount;
504  }
505 
510  private void createInnerWall(int tileId) {
511  // Randomly pick a wallable direction in that tile.
513  int dir;
514  do
515  dir = randDirections.get();
516  while (!isWallableDir(tileId, dir));
517  // Add wall to tileId and the adjacent tileId
518  Position neighbor = new Position(Position.toRow(tileId), Position.toCol(tileId), dir);
519  tiles[tileId].setWall(dir);
520  tiles[neighbor.getId()].setWall(Direction.opposite(dir));
521  // If we have loopGuard enable we populate walls also.
522  if (Session.loopGuard)
523  walls.add(new Edge(tileId, dir));
524  }
525 
531  private int createInnerWalls () {
532  ShuffledRange randTiles = new ShuffledRange(0, N*N);
533  for (int tileId, walls =0, shuffleMark =0 ; true ; ) {
534  // randomly pick a wallable tile.
535  do {
536  if ((tileId = randTiles.get())== Const.EOR) {
537  if (walls == shuffleMark) // Wallable tiles exhausted.
538  return walls;
539  else { // Re-shuffle and continue.
540  randTiles = new ShuffledRange(0, N*N);
541  shuffleMark =walls;
542  }
543  }
544  } while (!isWallable(tileId));
545  ++walls;
546  createInnerWall(tileId);
547  }
548  }
549 
559  private String getTileBody (int row, int col, int theseusTile, int minotaurTile) {
560  int tileId = Position.toID(row, col);
561  boolean T = (tileId == theseusTile) ? true : false;
562  boolean M = (tileId == minotaurTile) ? true : false;
563  int S = tiles[tileId].hasSupply(supplies);
564 
565  if (T && !M) return " T ";
566  else if (T && M) return "T+M";
567  else if (M) {
568  if (S == Const.noSupply) return " M ";
569  else return "M+s";
570  }
571  else if (S != Const.noSupply)
572  return String.format("s%02d", S+1);
573  else return " ";
574  }
575 
585  private void renderTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile) {
586  IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
587 
588  int tileId = Position.toID(row, col);
589  frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---" : "+ ";
590  frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
591  + getTileBody(row, col, theseusTile, minotaurTile);
592  frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---" : "+ ";
593  }
594 
605  private void renderSentinelTile(String[][] frame, int row, int col, int theseusTile, int minotaurTile ) {
606  IntFunction<Integer> toframe = (r)->{ return 2*r+1; };
607 
608  int tileId = Position.toID(row, col);
609  frame[toframe.apply(row)+1][col] = tiles[tileId].hasWall(Direction.UP) ? "+---+" : "+ +";
610  frame[toframe.apply(row) ][col] = (tiles[tileId].hasWall(Direction.LEFT)? "|" : " ")
611  + getTileBody(row, col, theseusTile, minotaurTile)
612  + (tiles[tileId].hasWall(Direction.RIGHT)? "|" : " ");
613  frame[toframe.apply(row)-1][col] = tiles[tileId].hasWall(Direction.DOWN) ? "+---+" : "+ +";
614  }
619  private IntFunction<Integer> leftTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)-1); };
620  private IntFunction<Integer> rightTileId = (id) -> { return Position.toID(Position.toRow(id), Position.toCol(id)+1); };
621  private IntFunction<Integer> upTileId = (id) -> { return Position.toID(Position.toRow(id)+1, Position.toCol(id) ); };
622  private IntFunction<Integer> downTileId = (id) -> { return Position.toID(Position.toRow(id)-1, Position.toCol(id) ); };
627  private int N;
628  private int S;
629  private int W;
630  private Tile[] tiles;
631  private Supply[] supplies;
632  private ArrayList<Edge> walls;
636  private ArrayList<Integer[]> moves;
638 }
Board()
The empty constructor for default initialization.
Definition: Board.java:33
void setW(int W)
Definition: Board.java:302
This class is the representation of the supplies in the game.
Definition: Supply.java:23
boolean isWallable(int tileId)
Predicate to check if a tile is Wallable.
Definition: Board.java:466
IntFunction< Integer > upTileId
Definition: Board.java:621
static final int RIGHT
East direction.
Definition: Common.java:68
int hasWalls()
Checks if the tile has walls and return the number of them.
Definition: Tile.java:183
int [][] getOpponentMoves(int playerId)
Boards utility to give access to other player moves.
Definition: Board.java:228
void createBoard(int theseusTile, int minotaurTile)
Creates the board with all the requested walls and supplies.
Definition: Board.java:98
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:605
This class is the representation of the games&#39;s board.
Definition: Board.java:26
Class to hold constant values for entire application.
Definition: Common.java:21
void pickSupply(Supply[] supplies, int supplyId)
Utility to find a supply in the supplies array and removes it.
Definition: Tile.java:204
Board(Board b)
Deep copy constructor.
Definition: Board.java:73
void setS(int S)
Definition: Board.java:301
int createBasicTileWalls()
This utility function create/allocate the tiles of the board and create the outer walls at the same t...
Definition: Board.java:486
ArrayList< Integer[]> moves
Definition: Board.java:636
void setSupplies(Supply[] supplies)
Definition: Board.java:315
Helper C++-like enumerator class to hold direction.
Definition: Common.java:66
void createTiles()
This function creates randomly all the tiles of the board.
Definition: Board.java:350
int W
The number of walls on the board.
Definition: Board.java:629
static final int End
Iterator style end of range direction (one place after the last)
Definition: Common.java:58
boolean hasWall(int direction)
Checks if the tile has wall in the requested direction.
Definition: Tile.java:169
static final int Step
Step for iterator style direction.
Definition: Common.java:59
String [][] getStringRepresentation(int theseusTile, int minotaurTile)
Returns a 2-D array with the string representation of the board.
Definition: Board.java:117
Tile [] tiles
Array to hold all the tiles for the board.
Definition: Board.java:630
void setMoves(ArrayList< Integer[]> moves)
Definition: Board.java:329
IntFunction< Integer > rightTileId
Definition: Board.java:620
ArrayList< Edge > walls
Array to hold all the walls using the edge representation required by the closed room preventing algo...
Definition: Board.java:632
ArrayList< Integer[]> getMoves()
Definition: Board.java:298
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:559
int createInnerWalls()
This utility creates the inner walls of the board.
Definition: Board.java:531
Tile [] getTiles()
Definition: Board.java:278
This class is the representation of the board&#39;s tile.
Definition: Tile.java:22
Provides a graph functionality for the room preventing algorithm.
Definition: Common.java:358
Helper C++ like enumerator class for direction ranged loops.
Definition: Common.java:56
Board(int N, int S)
The main constructor for the application.
Definition: Board.java:48
int getId()
Read access to id coordinate.
Definition: Common.java:143
int S
The number of the supplies on the board.
Definition: Board.java:628
static final int moveItems
The number of items return by move()
Definition: Common.java:27
int N
The size of each edge of the board.
Definition: Board.java:627
static int toRow(int id)
Takes Id coordinate and return the corresponding row coordinate.
Definition: Common.java:163
Supply [] supplies
Array to hold all the supplies on the board.
Definition: Board.java:631
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:585
boolean isRoomCreator(int tileId, int direction)
Predicate to check if a wall creates a closed room.
Definition: Board.java:387
boolean isWalkable(int row, int col, int direction)
Predicate to check if a direction is Walkable.
Definition: Board.java:178
ArrayList< Edge > getWalls()
Definition: Board.java:291
static final int EOR
Number to indicate the End Of Range.
Definition: Common.java:26
boolean isLeftSentinel(int tileId)
Definition: Board.java:336
static int toCol(int id)
Takes Id coordinate and return the corresponding column coordinate.
Definition: Common.java:171
IntFunction< Integer > leftTileId
Definition: Board.java:619
An Application wide board position implementation holding just the id coordinate. ...
Definition: Common.java:102
int hasSupply(Supply[] supplies)
Utility to check if the tile has a supply.
Definition: Tile.java:192
static final int maxTileWalls
Number of maximum walls for each tile on the board.
Definition: Common.java:22
static boolean loopGuard
When true a wall creation guard is added to prevent closed rooms inside the board.
Definition: Common.java:40
Supply [] getSupplies()
Definition: Board.java:284
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:390
boolean isUpSentinel(int tileId)
Definition: Board.java:338
void setWalls(ArrayList< Edge > walls)
Definition: Board.java:322
int generatePlayerId()
Utility function to create player IDs.
Definition: Board.java:243
boolean isWallableDir(int tileId, int direction)
Predicate to check if a tile direction is Wallable.
Definition: Board.java:432
void setWall(int direction)
Sets the tile&#39;s wall in the requested direction.
Definition: Tile.java:142
int dice()
A plain fair dice functionality provided by the board.
Definition: Board.java:214
static final int noSupply
Number to indicate the absent of supply.
Definition: Common.java:23
static int toID(int row, int col)
Takes row and column coordinates and return the calculated Id coordinate.
Definition: Common.java:154
void setTiles(Tile[] tiles)
Definition: Board.java:309
Application wide object to hold settings like values for the session.
Definition: Common.java:36
Class to create ranges of numbers.
Definition: Common.java:185
boolean hasSupply(int tileId)
Utility function to check if there is a supply on the tile or not.
Definition: Board.java:188
boolean isWalkable(int tileId, int direction)
Predicate to check if a direction is Walkable.
Definition: Board.java:159
static int boardSize
Default board&#39;s size (if no one set it via command line)
Definition: Common.java:37
static final int LEFT
West direction.
Definition: Common.java:70
static final int noTileId
Number to indicate wrong tileId.
Definition: Common.java:25
A utility class used for room prevent algorithm.
Definition: Common.java:295
int count(int v)
Counts the number of vertices on the graph with the value of v
Definition: Common.java:399
static final int UP
North direction.
Definition: Common.java:67
void updateMove(int[] m, int playerId)
Utility to update the moves of each player.
Definition: Board.java:258
static int opposite(int direction)
Utility to get the opposite direction.
Definition: Common.java:77
static final int DOWN
South direction.
Definition: Common.java:69
void createSupplies(int theseusTile, int minotaurTile)
This function create randomly the board&#39;s supplies.
Definition: Board.java:365
IntFunction< Integer > downTileId
Definition: Board.java:622
void createInnerWall(int tileId)
Create randomly a wall in the wallable selected tile.
Definition: Board.java:510
static final int Begin
Iterator style begin of range direction (starting north)
Definition: Common.java:57
void setN(int N)
Definition: Board.java:300
boolean isRightSentinel(int tileId)
Definition: Board.java:337
void printBoard(String[][] sBoard)
Print board utility.
Definition: Board.java:138
int tryPickSupply(int tileId)
Try to pick supply from a tile.
Definition: Board.java:201
Class to create shuffled ranges of numbers.
Definition: Common.java:240
boolean isDownSentinel(int tileId)
Definition: Board.java:339
int get()
Extract and return the first item from the range.
Definition: Common.java:218