/** * @file Common.java * * @author Christos Choutouridis AEM:8997 * @email cchoutou@ece.auth.gr */ package net.hoo2.auth.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 */ } /** * 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 */ 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 */ /** * Utility to get the opposite * @param direction Input direction * @return The opposite direction */ static int opposite (int direction) { return (direction+4)%End; } } /** * @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 row-column naming 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(); 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(); 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 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: * * _ V = 15 * / * +---+---+---+ 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. * | | | The edges are represented as vertices pairs. * + + + + <. * | | | \_ V = 7 * + +---+---+ * ^ ^ * V = 0 V = 3 * * @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 as 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 * is easy to find any closed loops. Using graph we transform the problem * of the closed room in the problem of finding a non simple graph. * * If the board has non connected wall structure then we need more than * one graph to represent it. * * An example graph from a board, starting from V=1 is: *
 *    6---7   8                (1)
 *    |       |               /  \
 *    3   4   5             (4)  (2)
 *    |   |   |                    \
 *    0   1---2                    (5)--(8)
 * 
*/ 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(); } /** * Constructor that transform an edge into graph. * @param e The edge to transform. */ Graph (Edge e) { V = e.getV1(); E = new ArrayList(); E.add(new Graph(e.getV2())); } /** Access to the current vertex */ int getV() { return V; } /** Access to the links of the current vertex */ ArrayList getE() { return E; } /** * Attach an edge into a graph IFF the graph already has a vertex * with the same value of 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 of one of the vertices of the edge. * @param e The edge to attach. * @param count An initial count value to feed to 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 value of `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 E; /**< A list of all the child nodes */ }