419 lines
13 KiB
Java
419 lines
13 KiB
Java
/**
|
|
* @file Common.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.Collections;
|
|
|
|
/**
|
|
* Class to hold constant values for entire application
|
|
*/
|
|
class Const {
|
|
static final int maxTileWalls = 2; /**< Number of maximum walls for each tile on the board */
|
|
static final int noSupply =-1; /**< Number to indicate the absent of supply */
|
|
static final int noTileId =-1; /**< Number to indicate wrong tileId */
|
|
static final int EOR =-1; /**< Number to indicate the End Of Range */
|
|
}
|
|
/**
|
|
* Application wide object to hold settings like values for the session.
|
|
*/
|
|
class Session {
|
|
static int boardSize = 15; /**< Default board's size (if no one set it via command line) */
|
|
static int supplySize = 4; /**< Default board's supply size (if no one set it via command line) */
|
|
static int maxRounds = 100; /**< Default number of rounds per game (if no one set it via command line) */
|
|
static boolean loopGuard = false; /**< When true a wall creation guard is added to prevent closed rooms inside the board */
|
|
static boolean interactive = false; /**< When true each round of the game requires user input */
|
|
}
|
|
|
|
/**
|
|
* Helper C++-like enumerator class to hold direction
|
|
*/
|
|
class Direction {
|
|
static final int UP =1; /**< North direction */
|
|
static final int RIGHT =3; /**< East direction */
|
|
static final int DOWN =5; /**< South direction */
|
|
static final int LEFT =7; /**< West direction */
|
|
|
|
/**
|
|
* Utility to get the opposite direction.
|
|
* @param direction Input direction
|
|
* @return The opposite direction
|
|
*/
|
|
static int opposite (int direction) { return (direction+4)%DirRange.End; }
|
|
}
|
|
|
|
/**
|
|
* Helper C++ like enumerator class for direction ranged loops.
|
|
*
|
|
* We can make use of this in loops like:
|
|
* <pre>
|
|
* for (int i=DirRange.Begin ; i<DirRange.End ; i += DirRange.Step) { }
|
|
*
|
|
* or
|
|
*
|
|
* Range directions = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
|
|
* </pre>
|
|
*/
|
|
class DirRange {
|
|
static final int Begin =1; /**< Iterator style begin of range direction (starting north) */
|
|
static final int End =8; /**< Iterator style end of range direction (one place after the last) */
|
|
static final int Step =2; /**< Step for iterator style direction */
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* An Application wide board position implementation holding just the id coordinate.
|
|
*
|
|
* Position is a helper class to enable us cope with the redundant position data (id and coordinates).
|
|
* This class provide both static conversion functionalities between id and coordinates
|
|
* and data representation in the coordinates system.
|
|
* For clarity we adopt a tileId convention.
|
|
*/
|
|
class Position {
|
|
|
|
/**
|
|
* Basic constructor from row-column coordinates
|
|
* @param row The row coordinate
|
|
* @param col The column coordinate
|
|
*/
|
|
Position(int row, int col) {
|
|
this.id = toID(row, col);
|
|
}
|
|
|
|
/**
|
|
* Basic constructor from Id
|
|
* @param tileId The id of tile
|
|
*/
|
|
Position(int tileId) {
|
|
this.id = tileId;
|
|
}
|
|
|
|
/**
|
|
* Constructor from row-column coordinates and a direction.
|
|
*
|
|
* This constructor creates a position relative to coordinates.
|
|
*
|
|
* @param row The row coordinate
|
|
* @param col The column coordinate
|
|
* @param direction The direction
|
|
*/
|
|
Position(int row, int col, int direction) {
|
|
switch (direction) {
|
|
case Direction.UP: this.id = toID(row+1, col); break;
|
|
case Direction.DOWN: this.id = toID(row-1, col); break;
|
|
case Direction.LEFT: this.id = toID(row, col-1); break;
|
|
case Direction.RIGHT:this.id = toID(row, col+1); break;
|
|
}
|
|
}
|
|
|
|
/** @name non-static API */
|
|
/** @{ */
|
|
int getRow() { return toRow(id); } /**< Read access to virtual row coordinate */
|
|
int getCol() { return toCol(id); } /**< Read access to virtual column coordinate */
|
|
int getId() { return id; } /**< Read access to id coordinate */
|
|
/** @} */
|
|
|
|
/** @name Static convention utilities */
|
|
/** @{ */
|
|
/**
|
|
* Takes row and column coordinates and return the calculated Id coordinate
|
|
* @param row The row coordinate
|
|
* @param col The column coordinate
|
|
* @return The converted value
|
|
*/
|
|
static int toID(int row, int col) {
|
|
return row * Session.boardSize + col;
|
|
}
|
|
|
|
/**
|
|
* Takes Id coordinate and return the corresponding row coordinate
|
|
* @param id The id coordinate
|
|
* @return The row coordinate
|
|
*/
|
|
static int toRow(int id){
|
|
return id / Session.boardSize;
|
|
}
|
|
/**
|
|
* Takes Id coordinate and return the corresponding column coordinate
|
|
* @param id The id coordinate
|
|
* @return The column coordinate
|
|
*/
|
|
static int toCol(int id) {
|
|
return id % Session.boardSize;
|
|
}
|
|
/** @} */
|
|
|
|
/** @name private data types */
|
|
/** @{ */
|
|
private int id; /**< The id coordinate of the constructed Position object */
|
|
/** @} */
|
|
}
|
|
|
|
/**
|
|
* Class to create ranges of numbers
|
|
*/
|
|
class Range {
|
|
/**
|
|
* Create the range [begin, end)
|
|
* @param begin The first item on the range
|
|
* @param end The item after the last on the range
|
|
*/
|
|
Range (int begin, int end) {
|
|
numbers = new ArrayList<Integer>();
|
|
init (begin, end, 1);
|
|
}
|
|
/**
|
|
* Create the range [begin, end) using step as interval between items.
|
|
* @param begin The first item on the range
|
|
* @param end The item after the last on the range
|
|
* @param step The interval between items
|
|
*/
|
|
Range(int begin, int end, int step) {
|
|
numbers = new ArrayList<Integer>();
|
|
init (begin, end, step);
|
|
}
|
|
|
|
/**
|
|
* Common utility to create the range for all constructors
|
|
*/
|
|
private void init (int begin, int end, int step) {
|
|
numbers.clear();
|
|
for (int i=begin ; i<end ; i+=step)
|
|
numbers.add(i);
|
|
}
|
|
/**
|
|
* Extract and return the first item from the range.
|
|
* @return The first item of the range or Const.noTileId if there is none.
|
|
*/
|
|
int get () {
|
|
if (!numbers.isEmpty())
|
|
return numbers.remove(0);
|
|
return Const.EOR;
|
|
}
|
|
|
|
/** @name protected data types */
|
|
/** @{ */
|
|
protected ArrayList<Integer> numbers; /**< handle to range */
|
|
/** @} */
|
|
}
|
|
|
|
/**
|
|
* Class to create shuffled ranges of numbers
|
|
*/
|
|
class ShuffledRange extends Range {
|
|
/**
|
|
* Create a shuffled version of range [begin, end)
|
|
* @param begin The first item on the range
|
|
* @param end The item after the last on the range
|
|
*/
|
|
ShuffledRange(int begin, int end) {
|
|
super(begin, end); // Delegate
|
|
Collections.shuffle(numbers);
|
|
}
|
|
/**
|
|
* Create a shuffled version of the range [begin, end)
|
|
* using step as interval between items.
|
|
*
|
|
* @param begin The first item on the range
|
|
* @param end The item after the last on the range
|
|
* @param step The interval between items
|
|
*/
|
|
ShuffledRange(int begin, int end, int step) {
|
|
super(begin, end, step); // Delegate
|
|
Collections.shuffle(numbers);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* A utility class used for room prevent algorithm.
|
|
*
|
|
* This class is the wall representation we use in the room preventing algorithm.
|
|
* In this algorithm we represent the crosses between tiles as nodes (V) of a graph and the
|
|
* walls as edges. So for example:
|
|
* <pre>
|
|
* 12--13--14---15
|
|
* | |
|
|
* 8 9--10 11
|
|
* | | |
|
|
* 4 5 6 7
|
|
* | | |
|
|
* 0 1---2---3
|
|
* </pre>
|
|
* In this example we have a 4x4=16 vertices board(nodes) and 14 edges(walls).
|
|
* To represent the vertices on the board we use the same trick as the tileId
|
|
*
|
|
* V = Row*(N+1) + Column, where N is the board's tile size.
|
|
*
|
|
* The edges are represented as vertices pairs. For example (0, 4) or (13, 14).
|
|
*
|
|
* @note
|
|
* Beside the fact that we prefer this kind of representation of the walls in
|
|
* the application we use the one that is suggested from the assignment. This is
|
|
* used only in room preventing algorithm.
|
|
* @note
|
|
* Using this kind of representation we don't have any more the "problem"
|
|
* of setting the wall in both neighbor tiles.
|
|
*/
|
|
class Edge {
|
|
/**
|
|
* This constructor acts as the interface between the application's wall
|
|
* representation and the one based on graph.
|
|
* @param tileId The tile id of the wall.
|
|
* @param direction The direction of the tile where the wall should be.
|
|
*/
|
|
Edge(int tileId, int direction) {
|
|
int N = Session.boardSize +1;
|
|
switch (direction) {
|
|
case Direction.UP:
|
|
v1= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId);
|
|
v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId) + 1;
|
|
break;
|
|
case Direction.DOWN:
|
|
v1= (Position.toRow(tileId))*N + Position.toCol(tileId);
|
|
v2= (Position.toRow(tileId))*N + Position.toCol(tileId) + 1;
|
|
break;
|
|
case Direction.LEFT:
|
|
v1= (Position.toRow(tileId))*N + Position.toCol(tileId);
|
|
v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId);
|
|
break;
|
|
case Direction.RIGHT:
|
|
v1= (Position.toRow(tileId))*N + Position.toCol(tileId) + 1;
|
|
v2= (Position.toRow(tileId) + 1)*N + Position.toCol(tileId) +1;
|
|
break;
|
|
}
|
|
}
|
|
/** A deep copy contructor */
|
|
Edge(Edge e) {
|
|
v1 = e.getV1();
|
|
v2 = e.getV2();
|
|
}
|
|
/** Access of the first node of the edge */
|
|
int getV1() { return v1; }
|
|
/** Access of the second node of the edge */
|
|
int getV2() { return v2; }
|
|
|
|
private int v1; /**< First vertex of the edge */
|
|
private int v2; /**< Second vertex of the edge */
|
|
}
|
|
|
|
/**
|
|
* @brief
|
|
* Provides a graph functionality for the room preventing algorithm.
|
|
* We use a graph to represent the wall structure of the walls. This way
|
|
* its easy to find any closed loops. Using graph we transform the problem
|
|
* of the closed room into the problem of finding a non simple graph.
|
|
*
|
|
* If the board has non connected wall structure then we would need a non
|
|
* coherent graph to represent it. This class provides constroctors and
|
|
* methods to create coherent graphs
|
|
*
|
|
* An example of the biggest coherent graph we can create from the board bellow,
|
|
* starting from V=1 is:
|
|
* <pre>
|
|
* 6---7 8 (1)
|
|
* | | / \
|
|
* 3 4 5 (4) (2)
|
|
* | | | \
|
|
* 0 1---2 (5)--(8)
|
|
* </pre>
|
|
*/
|
|
class Graph {
|
|
/**
|
|
* Constructs a node of the graph using the value of a vertex(node).
|
|
* @param v The verteg to attach.
|
|
*/
|
|
Graph (int v) {
|
|
V = v;
|
|
E = new ArrayList<Graph>();
|
|
}
|
|
/**
|
|
* Constructor that transform an edge into graph.
|
|
* @param e The edge to transform.
|
|
*/
|
|
Graph (Edge e) {
|
|
V = e.getV1();
|
|
E = new ArrayList<Graph>();
|
|
E.add(new Graph(e.getV2()));
|
|
}
|
|
|
|
/** Access to the current vertex */
|
|
int getV() { return V; }
|
|
/** Access to the links of the current vertex */
|
|
ArrayList<Graph> getE() { return E; }
|
|
|
|
/**
|
|
* Attach an edge into a graph IFF the graph already has a vertex
|
|
* with the same value as one of the vertices of the edge.
|
|
* @param e The edge to attach.
|
|
* @return The status of the operation.
|
|
* @arg True on success
|
|
* @arg False on failure
|
|
*/
|
|
boolean attach (Edge e) {
|
|
return tryAttach(e, 0) > 0;
|
|
}
|
|
|
|
/**
|
|
* Counts the number of vertices on the graph with the value of `v`
|
|
* @param v The vertex to count
|
|
* @return The number of vertices with value `v`
|
|
*/
|
|
int count (int v) {
|
|
return tryCount (v, 0);
|
|
}
|
|
|
|
/**
|
|
* Recursive algorithm that tries to attach an edge into a graph
|
|
* IFF the graph already has a vertex with the same value as one
|
|
* of the vertices of the edge.
|
|
*
|
|
* @param e The edge to attach.
|
|
* @param count An initial count value to feed the algorithm.
|
|
* @return The status of the operation.
|
|
* @arg True on success
|
|
* @arg False on failure
|
|
*/
|
|
private int tryAttach (Edge e, int count) {
|
|
for (Graph n: E)
|
|
count = n.tryAttach (e, count);
|
|
if (V == e.getV1()) {
|
|
E.add(new Graph(e.getV2()));
|
|
++count;
|
|
}
|
|
if (V == e.getV2()) {
|
|
E.add(new Graph(e.getV1()));
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Recursive algorithm that tries to count the number of vertices
|
|
* on the graph with the same value as `v`.
|
|
*
|
|
* @param v The vertex to count
|
|
* @param count An initial count value to feed to the algorithm.
|
|
* @return The number of vertices with value `v`
|
|
*/
|
|
private int tryCount (int v, int count) {
|
|
for (Graph n: E)
|
|
count = n.tryCount (v, count);
|
|
if (V == v)
|
|
return ++count;
|
|
return count;
|
|
}
|
|
|
|
private int V; /**< The value of the current vertex/node */
|
|
private ArrayList<Graph> E; /**< A list of all the child nodes */
|
|
}
|