diff --git a/ds_project_2020-2021_PartB_v3.pdf b/ds_project_2020-2021_PartB_v3.pdf new file mode 100644 index 0000000..4f2c352 Binary files /dev/null and b/ds_project_2020-2021_PartB_v3.pdf differ diff --git a/src/host/labyrinth/Board.java b/src/host/labyrinth/Board.java index c127a0a..37d5726 100644 --- a/src/host/labyrinth/Board.java +++ b/src/host/labyrinth/Board.java @@ -13,6 +13,7 @@ package host.labyrinth; import java.util.ArrayList; +import java.util.Arrays; import java.util.function.IntFunction; /** @@ -36,6 +37,7 @@ class Board { tiles = null; supplies =null; walls = new ArrayList(); + moves = new ArrayList(); } /** @@ -52,6 +54,7 @@ class Board { tiles = new Tile[N*N]; supplies = new Supply[S]; walls = new ArrayList(); + moves = new ArrayList(); } /** @@ -77,6 +80,9 @@ class Board { this.supplies = b.supplies.clone(); for (Edge it: b.walls) this.walls.add(new Edge(it)); + for (Integer[] m : b.moves) { + this.moves.add(m); + } } /** @} */ @@ -174,6 +180,15 @@ class Board { && !(Position.toID(row, col) == 0 && direction == Direction.DOWN); } + /** + * Utility function to check if there is a supply on the tile or not + * @param tileId The tile to check + * @return Yes/no + */ + boolean hasSupply (int tileId) { + return (Const.noSupply != tiles[tileId].hasSupply(supplies)) ? true : false; + } + /** * Try to pick supply from a tile. If succeed it also erases the * supply from the board. @@ -191,6 +206,7 @@ class Board { return supplyId; } + /** * A plain fair dice functionality provided by the board. * @return A random direction; @@ -202,6 +218,27 @@ class Board { /** @return the size of each site of the board. */ int size () { return N; } + + int[][] getOpponentMoves (int playerId) { + int[][] ret = new int[moves.size()-1][Const.moveItems]; + int ii= 0, ri =0; + for (Integer[] m : moves) { + if (ii != playerId) + ret[ri++] = Arrays.stream(m).mapToInt(i->i).toArray(); + ++ii; + } + return ret; + } + + int generatePlayerId () { + moves.add(null); + return moves.size() -1; + } + + void updateMove(int[] m, int playerId) { + moves.set(playerId, Arrays.stream(m).boxed().toArray(Integer[]::new)); + } + /** @} */ /** @@ -233,6 +270,13 @@ class Board { */ ArrayList getWalls() { return walls; } + /** + * @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure". + * see also here + * @return Reference to inner walls array. + */ + ArrayList getMoves() { return moves; } + void setN(int N) { this.N = N; } void setS(int S) { this.S = S; } void setW(int W) { this.W = W; } @@ -257,6 +301,13 @@ class Board { */ void setWalls (ArrayList walls) { this.walls= walls; } + /** + * @param moves Reference to moves that we want to act as replacement for the inner moves vector. + * @note Use with care. + * Any call to this function will probably add memory for the garbage collector. + */ + void setMoves(ArrayList moves) { this.moves =moves; } + /** @} */ @@ -562,5 +613,6 @@ class Board { * Array to hold all the walls using the edge representation * required by the closed room preventing algorithm. */ + private ArrayList moves; /** @} */ } diff --git a/src/host/labyrinth/Common.java b/src/host/labyrinth/Common.java index 87177ee..762e925 100644 --- a/src/host/labyrinth/Common.java +++ b/src/host/labyrinth/Common.java @@ -13,6 +13,7 @@ package host.labyrinth; import java.util.ArrayList; import java.util.Collections; +import java.util.function.IntFunction; /** * Class to hold constant values for entire application @@ -20,8 +21,14 @@ import java.util.Collections; 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 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 moveItems =4; /**< The number of items return by move() */ + static final int viewDistance =3; /**< The max distance of the Heuristic player's ability to see */ + + static final double opponentFactor =1.0; + static final double supplyFactor =0.65; } /** * Application wide object to hold settings like values for the session. @@ -34,23 +41,6 @@ class Session { 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. * @@ -69,6 +59,36 @@ class DirRange { static final int Step =2; /**< Step for iterator style direction */ } +/** + * 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; } + + 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. @@ -200,6 +220,13 @@ class Range { return Const.EOR; } + /** + * @return The size of the underline structure + */ + int size () { + return numbers.size(); + } + /** @name protected data types */ /** @{ */ protected ArrayList numbers; /**< handle to range */ diff --git a/src/host/labyrinth/Game.java b/src/host/labyrinth/Game.java index ca6f0f1..f9083a9 100644 --- a/src/host/labyrinth/Game.java +++ b/src/host/labyrinth/Game.java @@ -155,8 +155,8 @@ public class Game { // Create a game, a board and 2 players. Game game = new Game(); Board board = new Board(Session.boardSize, Session.supplySize); - Player T = new Player(1, "Theseus", true, board, 0); - Player M = new Player(2, "Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2)); + Player T = new HeuristicPlayer("Theseus", true, board, 0); + Player M = new Player("Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2)); // Populate data to the board board.createBoard(T.playerTileId(), M.playerTileId()); diff --git a/src/host/labyrinth/HeuristicPlayer.java b/src/host/labyrinth/HeuristicPlayer.java new file mode 100644 index 0000000..48c5faa --- /dev/null +++ b/src/host/labyrinth/HeuristicPlayer.java @@ -0,0 +1,191 @@ +/** + * @file Player.java + * + * @author + * Anastasia Foti AEM:8959 + * + * + * @author + * Christos Choutouridis AEM:8997 + * + */ + +package host.labyrinth; + +import java.util.ArrayList; +//import java.util.Arrays; + +/** + * @brief + * This class represents the game's player who cheats. + */ +class HeuristicPlayer extends Player { + + /** @name Constructors */ + /** @{ */ + public HeuristicPlayer(String name, boolean champion, Board board, int row, int column) { + super(name, champion, board, row, column); + path = new ArrayList(); + } + + public HeuristicPlayer(String name, boolean champion, Board board, int tileId) { + super(name, champion, board, tileId); + path = new ArrayList(); + } + /** @} */ + + /** @name Board's main application interface */ + /** @{ */ + + /** + * Utility to get the distance of a possible supply in some direction + * @param currentPos The current position of the player + * @param direction The direction to check + * @return The distance or Const.noSupply + */ + int supplyInDirection(int currentPos, int direction) { + Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); + + for (int i=0 ; board.isWalkable(pos.getId(), direction) && i M) { + M = eval[i]; + M_idx = i; + } + } + return eval_dir[M_idx]; + } + + boolean isUnevaluated (double[] eval, int N) { + for (int i =0 ; i + *
  • int[0]: The tileId of the final player's position. + *
  • int[1]: The row of the final player's position. + *
  • int[2]: The column of the final player's position. + *
  • int[3]: The supplyId in case player picked one (Const.noSupply otherwise). + * + */ + int[] move(int id) { + // Initialize return array with the current data + int[] ret = new int[Const.moveItems]; + ret[0] = getNextMove(id); + ret[1] = y = Position.toRow(ret[0]); + ret[2] = x = Position.toCol(ret[0]); + int supplyFlag = 0; + // In case of a champion player, try also to pick a supply + if (champion && (ret[3] = board.tryPickSupply(ret[0])) != Const.noSupply) { + ++score; // keep score + ++supplyFlag; + System.out.println(name + ":\t*Found a supply. [score: " + score + "]"); + } + board.updateMove(ret, playerId); + + // Update supply and opponent distance + int dir = Direction.get(id, ret[0]); + int smin =DirRange.End, omin =DirRange.End; + for (int d = DirRange.Begin ; d path; /**< our history + * The integer[] format is: + * { dice, tookSupply, SupplyDistance, OpponentDistance} + */ + + /** @} */ +} diff --git a/src/host/labyrinth/Player.java b/src/host/labyrinth/Player.java index 5a964a4..0311610 100644 --- a/src/host/labyrinth/Player.java +++ b/src/host/labyrinth/Player.java @@ -29,14 +29,16 @@ class Player { * @param row The row coordinate of initial player position * @param column The column coordinate of initial player's position */ - Player(int id, String name, boolean champion, Board board, int row, int column) { - this.playerId = id; + Player(String name, boolean champion, Board board, int row, int column) { + this.playerId = board.generatePlayerId(); this.name = name; this.board = board; this.score = 0; this.x = column; this.y = row; this.champion = champion; + int[] m = {Position.toID(row, column), row, column, Const.noSupply}; + board.updateMove(m, playerId); } /** @@ -47,14 +49,16 @@ class Player { * @param board Reference to the board of the game * @param tileId The tileId coordinate of player's initial position */ - Player(int id, String name, boolean champion, Board board, int tileId) { - this.playerId = id; + Player(String name, boolean champion, Board board, int tileId) { + this.playerId = board.generatePlayerId(); this.name = name; this.board = board; this.score = 0; this.x = Position.toCol(tileId); this.y = Position.toRow(tileId); this.champion = champion; + int[] m = {tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply}; + board.updateMove(m, playerId); } /** @} */ @@ -80,7 +84,11 @@ class Player { */ int[] move(int id) { // Initialize return array with the current data - int[] ret = {id, Position.toRow(id), Position.toCol(id), Const.noSupply}; + int[] ret = new int[Const.moveItems]; + ret[0] = id; + ret[1] = Position.toRow(id); + ret[2] = Position.toCol(id); + ret[3] = Const.noSupply; int diceDirection = board.dice(); // throw the dice if (board.isWalkable(id, diceDirection)) { // The result is walkable @@ -94,6 +102,7 @@ class Player { ++score; // keep score System.out.println(name + ":\t*Found a supply. [score: " + score + "]"); } + board.updateMove(ret, playerId); } else System.out.println(name + ":\t*Can not move."); @@ -143,12 +152,12 @@ class Player { /** @name Class data */ /** @{ */ - private int playerId; /**< The unique identifier of the player */ - private String name; /**< The name of the player */ - private Board board; /**< Reference to the session's boards */ - private int score; /**< The current score of the player */ - private int x; /**< The column coordinate of the player on the board */ - private int y; /**< The row coordinate of the player on the board */ - private boolean champion; /**< Champion indicate a player who plays against the Minotaur */ + protected int playerId; /**< The unique identifier of the player */ + protected String name; /**< The name of the player */ + protected Board board; /**< Reference to the session's boards */ + protected int score; /**< The current score of the player */ + protected int x; /**< The column coordinate of the player on the board */ + protected int y; /**< The row coordinate of the player on the board */ + protected boolean champion; /**< Champion indicate a player who plays against the Minotaur */ /** @} */ }