|
@@ -7,6 +7,7 @@ |
|
|
|
|
|
|
|
|
package net.hoo2.auth.labyrinth; |
|
|
package net.hoo2.auth.labyrinth; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.ArrayList; |
|
|
import java.util.function.IntFunction; |
|
|
import java.util.function.IntFunction; |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -24,26 +25,27 @@ class Board { |
|
|
* The empty constructor for default initialization |
|
|
* The empty constructor for default initialization |
|
|
*/ |
|
|
*/ |
|
|
Board() { |
|
|
Board() { |
|
|
this.N = this.S = this.W = 0; |
|
|
|
|
|
this.tiles = null; |
|
|
|
|
|
this.supplies =null; |
|
|
|
|
|
|
|
|
this.N = 0; |
|
|
|
|
|
this.S = 0; |
|
|
|
|
|
this.W = 0; |
|
|
|
|
|
tiles = null; |
|
|
|
|
|
supplies =null; |
|
|
|
|
|
walls = new ArrayList<Edge>(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* The main constructor for the application |
|
|
* The main constructor for the application |
|
|
* @param N The size of each edge of the board |
|
|
* @param N The size of each edge of the board |
|
|
* @param S The number of supplies on the board |
|
|
* @param S The number of supplies on the board |
|
|
* @param W The number of walls on the board |
|
|
|
|
|
*/ |
|
|
*/ |
|
|
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]"; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Board(int N, int S) { |
|
|
|
|
|
assert (N%2 != 0) : "Board's size has to be an odd number."; |
|
|
this.N = Session.boardSize = N; |
|
|
this.N = Session.boardSize = N; |
|
|
this.S = S; |
|
|
this.S = S; |
|
|
this.W = W; |
|
|
|
|
|
|
|
|
this.W = 0; |
|
|
tiles = new Tile[N*N]; |
|
|
tiles = new Tile[N*N]; |
|
|
supplies = new Supply[S]; |
|
|
supplies = new Supply[S]; |
|
|
|
|
|
walls = new ArrayList<Edge>(); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -67,6 +69,7 @@ class Board { |
|
|
// Clone arrays |
|
|
// Clone arrays |
|
|
this.tiles = b.tiles.clone(); |
|
|
this.tiles = b.tiles.clone(); |
|
|
this.supplies = b.supplies.clone(); |
|
|
this.supplies = b.supplies.clone(); |
|
|
|
|
|
this.walls = b.walls; |
|
|
} |
|
|
} |
|
|
/** @} */ |
|
|
/** @} */ |
|
|
|
|
|
|
|
@@ -79,7 +82,7 @@ class Board { |
|
|
* @param theseusTile |
|
|
* @param theseusTile |
|
|
* @param minotaurTile |
|
|
* @param minotaurTile |
|
|
*/ |
|
|
*/ |
|
|
void createBoard(int theseusTile, int minotaurTile) throws Exception { |
|
|
|
|
|
|
|
|
void createBoard(int theseusTile, int minotaurTile) { |
|
|
createTiles(); |
|
|
createTiles(); |
|
|
createSupplies(theseusTile, minotaurTile); |
|
|
createSupplies(theseusTile, minotaurTile); |
|
|
} |
|
|
} |
|
@@ -220,6 +223,15 @@ class Board { |
|
|
void setSupplies(Supply[] supplies) { this.supplies= supplies; } |
|
|
void setSupplies(Supply[] supplies) { this.supplies= supplies; } |
|
|
/** @} */ |
|
|
/** @} */ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** @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 |
|
|
* @name private functionality of the object |
|
|
*/ |
|
|
*/ |
|
@@ -228,11 +240,11 @@ class Board { |
|
|
/** |
|
|
/** |
|
|
* This function creates randomly all the tiles of the board |
|
|
* This function creates randomly all the tiles of the board |
|
|
*/ |
|
|
*/ |
|
|
private void createTiles() throws Exception { |
|
|
|
|
|
int wallPool = W; |
|
|
|
|
|
wallPool -= createBasicTileWalls (); // First create tiles with outer walls |
|
|
|
|
|
if (createInnerWalls(wallPool) > 0) // Create inner walls with the rest of the requested walls |
|
|
|
|
|
throw new Exception("Can not create the requested number of walls"); |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -254,13 +266,43 @@ class Board { |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** @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); } |
|
|
|
|
|
/** @} */ |
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Predicate to check if a wall creates a closed room. |
|
|
|
|
|
* |
|
|
|
|
|
* This algorithm has a complexity of O(N^2) where N represents the total |
|
|
|
|
|
* number of tiles it should be used with care. |
|
|
|
|
|
* |
|
|
|
|
|
* @param tileId The tileId of the wall where the wall is. |
|
|
|
|
|
* @param direction The wall's relative direction from the tile. |
|
|
|
|
|
* @return True if the wall creates a closed room, false otherwise. |
|
|
|
|
|
*/ |
|
|
|
|
|
private boolean createsClosedRoom (int tileId, int direction) { |
|
|
|
|
|
// Get a snapshot of the current walls |
|
|
|
|
|
ArrayList<Edge> w = new ArrayList<Edge>(); |
|
|
|
|
|
for (Edge it : walls) |
|
|
|
|
|
w.add(new Edge(it)); |
|
|
|
|
|
// Create a graph from the current wall(edge) |
|
|
|
|
|
// and populate the graph with all the edges we can attach. |
|
|
|
|
|
Graph g = new Graph(new Edge(tileId, direction)); |
|
|
|
|
|
int size; |
|
|
|
|
|
do { |
|
|
|
|
|
size = w.size(); |
|
|
|
|
|
for (int i =0, S=w.size() ; i<S ; ++i) |
|
|
|
|
|
if (g.attach(w.get(i))) { |
|
|
|
|
|
w.remove(i); |
|
|
|
|
|
--i; --S; |
|
|
|
|
|
} |
|
|
|
|
|
} while (size != w.size()); |
|
|
|
|
|
|
|
|
|
|
|
// Search if a vertex is attached 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`. |
|
|
* Predicate to check if a tile direction is `Wallable`. |
|
@@ -270,6 +312,7 @@ class Board { |
|
|
* <li>The wall is not the DOWN wall from tile (0, 0). |
|
|
* <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>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 neighbor in this direction has at most `Const.maxTileWalls -1` walls. |
|
|
|
|
|
* <li>The wall does not create a closed room (Optional requirement). |
|
|
* </ul> |
|
|
* </ul> |
|
|
* |
|
|
* |
|
|
* @note |
|
|
* @note |
|
@@ -286,12 +329,22 @@ class Board { |
|
|
if (tiles[tileId].hasWall(direction)) |
|
|
if (tiles[tileId].hasWall(direction)) |
|
|
return false; |
|
|
return false; |
|
|
switch (direction) { |
|
|
switch (direction) { |
|
|
case Direction.UP: return (tiles[upTileId.apply(tileId)].hasWalls() < Const.maxTileWalls); |
|
|
|
|
|
case Direction.DOWN: return (tiles[downTileId.apply(tileId)].hasWalls() < Const.maxTileWalls); |
|
|
|
|
|
case Direction.LEFT: return (tiles[leftTileId.apply(tileId)].hasWalls() < Const.maxTileWalls); |
|
|
|
|
|
case Direction.RIGHT:return (tiles[rightTileId.apply(tileId)].hasWalls() < Const.maxTileWalls); |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
} |
|
|
return false; |
|
|
|
|
|
|
|
|
if (Session.loopGuard && createsClosedRoom(tileId, direction)) |
|
|
|
|
|
return false; |
|
|
|
|
|
return true; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -326,16 +379,22 @@ class Board { |
|
|
* @return The number of walls created from the utility. |
|
|
* @return The number of walls created from the utility. |
|
|
*/ |
|
|
*/ |
|
|
private int createBasicTileWalls () { |
|
|
private int createBasicTileWalls () { |
|
|
int tileCount =0; |
|
|
|
|
|
|
|
|
int wallCount =0; |
|
|
for (int i =0 ; i< tiles.length ; ++i) { |
|
|
for (int i =0 ; i< tiles.length ; ++i) { |
|
|
boolean up = isUpSentinel(i); |
|
|
boolean up = isUpSentinel(i); |
|
|
boolean down = isDownSentinel(i) && (i != 0); |
|
|
boolean down = isDownSentinel(i) && (i != 0); |
|
|
boolean left = isLeftSentinel(i); |
|
|
boolean left = isLeftSentinel(i); |
|
|
boolean right = isRightSentinel(i); |
|
|
boolean right = isRightSentinel(i); |
|
|
tileCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0)); |
|
|
|
|
|
|
|
|
wallCount += ((up?1:0) + (down?1:0) + (left?1:0) + (right?1:0)); |
|
|
tiles[i] = new Tile (i, up, down, left, right); |
|
|
tiles[i] = new Tile (i, up, down, left, right); |
|
|
|
|
|
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 tileCount; |
|
|
|
|
|
|
|
|
return wallCount; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -352,6 +411,8 @@ class Board { |
|
|
Position neighbor = new Position(Position.toRow(tileId), Position.toCol(tileId), dir); |
|
|
Position neighbor = new Position(Position.toRow(tileId), Position.toCol(tileId), dir); |
|
|
tiles[tileId].setWall(dir); |
|
|
tiles[tileId].setWall(dir); |
|
|
tiles[neighbor.getId()].setWall(Direction.opposite(dir)); |
|
|
tiles[neighbor.getId()].setWall(Direction.opposite(dir)); |
|
|
|
|
|
if (Session.loopGuard) |
|
|
|
|
|
walls.add(new Edge(tileId, dir)); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -360,23 +421,23 @@ class Board { |
|
|
* @param walls The number of walls to create |
|
|
* @param walls The number of walls to create |
|
|
* @return The number of walls failed to create. |
|
|
* @return The number of walls failed to create. |
|
|
*/ |
|
|
*/ |
|
|
private int createInnerWalls (int walls) { |
|
|
|
|
|
|
|
|
private int createInnerWalls () { |
|
|
ShuffledRange randTiles = new ShuffledRange(0, N*N); |
|
|
ShuffledRange randTiles = new ShuffledRange(0, N*N); |
|
|
for (int tileId, i =0, shuffleMark =0 ; i<walls ; ++i) { |
|
|
|
|
|
|
|
|
for (int tileId, i =0, walls =0, shuffleMark =0 ; true ; ) { |
|
|
// randomly pick a wallable tile. |
|
|
// randomly pick a wallable tile. |
|
|
do { |
|
|
do { |
|
|
if ((tileId = randTiles.get())== Const.noTileId) { |
|
|
if ((tileId = randTiles.get())== Const.noTileId) { |
|
|
if (i == shuffleMark) // Wallable tiles exhausted. |
|
|
if (i == shuffleMark) // Wallable tiles exhausted. |
|
|
return walls - i; |
|
|
|
|
|
|
|
|
return walls; |
|
|
else { // Re-shuffle and continue. |
|
|
else { // Re-shuffle and continue. |
|
|
randTiles = new ShuffledRange(0, N*N); |
|
|
randTiles = new ShuffledRange(0, N*N); |
|
|
shuffleMark =i; |
|
|
shuffleMark =i; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} while (!isWallable(tileId)); |
|
|
} while (!isWallable(tileId)); |
|
|
|
|
|
++walls; |
|
|
createInnerWall(tileId); |
|
|
createInnerWall(tileId); |
|
|
} |
|
|
} |
|
|
return 0; |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
@@ -456,10 +517,14 @@ class Board { |
|
|
|
|
|
|
|
|
/** @name Class data */ |
|
|
/** @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 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. |
|
|
|
|
|
*/ |
|
|
/** @} */ |
|
|
/** @} */ |
|
|
} |
|
|
} |