/** * @file Common.java * * @author * Anastasia Foti AEM:8959 * * * @author * Christos Choutouridis AEM:8997 * */ package host.labyrinth; import java.util.ArrayList; import java.util.Collections; //import java.util.function.IntFunction; /** * Class to hold constant values for entire application */ class Const { static final int numOfPlayers = 2; 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 noOpponent =-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 */ static final int viewDistance =3; /**< The max distance of the Heuristic player's ability to see */ static final int noView = viewDistance+1; /** Parameters to control move evaluation */ /** @{ */ static final double opponentFactor = 1.0; /**< opponent distance factor */ static final double supplyFactor = 0.65; /**< supply distance factor */ static final double preMoveFactor = 0.65; /**< pre move distances factor */ static final double postMoveFactor = 0.35; /**< post move distances factor */ static final int minimaxTreeDepth = 4; /**< The maximum depth of the minimax tree */ /** @} */ } /** * 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 for direction ranged loops. * * We can make use of this in loops like: *
 * for (int i=DirRange.Begin ; i
 */
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 */
   static final int  numOfDirections =4;
}

/**
 * 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  NONE  =8;   /**< No direction */

   /**
    * Utility to get the opposite direction.
    * @param direction  Input direction
    * @return           The opposite direction
    */
   static int opposite (int direction) {
      return (direction != NONE) ? (direction+4)%DirRange.End : NONE;
   }

   static int get (int fromId, int toId) {
      if      (Position.toID(Position.toRow(fromId),   Position.toCol(fromId)-1) == toId)
         return Direction.LEFT;
      else if (Position.toID(Position.toRow(fromId),   Position.toCol(fromId)+1) == toId)
         return Direction.RIGHT;
      else if (Position.toID(Position.toRow(fromId)+1, Position.toCol(fromId)  ) == toId)
         return Direction.UP;
      else if (Position.toID(Position.toRow(fromId)-1, Position.toCol(fromId)  ) == toId)
         return Direction.DOWN;
      else
         return DirRange.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 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;
         case Direction.NONE: this.id = toID(row, col);     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:
 * 
 *   12--13--14---15
 *    |           |
 *    8   9--10   11
 *    |       |   |
 *    4   5   6   7
 *    |   |       |
 *    0   1---2---3
 * 
* 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 constructors 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: *
 *    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 vertex 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 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 E; /**< A list of all the child nodes */ }