Labyrinth/src/host/labyrinth/Common.java
2020-11-08 22:51:08 +02:00

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 */
}