/** * @file Player.java * * @author * Anastasia Foti AEM:8959 * * * @author * Christos Choutouridis AEM:8997 * */ package host.labyrinth; import java.util.ArrayList; /** * @brief * This class represents the game's player */ class Player { /** Helper variables to keep track of the move() return values @see move() */ static final int MOVE_DATA_SIZE = 4; /**< The move return data array size */ static final int MOVE_TILE_ID = 0; /**< Index of the tileId information of the move */ static final int MOVE_ROW = 1; /**< The index of row information */ static final int MOVE_COLUMN = 2; /**< The index of column information */ static final int MOVE_DICE = 3; /**< The index of dice information */ /** @name Constructors */ /** @{ */ /** * Create a new player and put him at the row-column coordinates * @param name The name of the player * @param champion Flag to indicate if a player is a `champion` * @param board Reference to the board of the game * @param row The row coordinate of initial player position * @param column The column coordinate of initial player's position */ Player(String name, boolean champion, Board board, int row, int column) throws Exception { this.playerId = board.generatePlayerId(); this.name = name; this.board = board; this.score = 0; this.x = column; this.y = row; this.champion = champion; this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is the worst. this.path = new ArrayList(); int[] m = { Position.toID(row, column), row, column, Const.noSupply }; board.updateMove(m, playerId); } /** * Create a new player and put him at the row-column coordinates * @param name The name of the player * @param champion Flag to indicate if a player is a `champion` * @param board Reference to the board of the game * @param tileId The tileId coordinate of player's initial position */ Player(String name, boolean champion, Board board, int tileId) throws Exception { 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; this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is the worst. this.path = new ArrayList(); int[] m = { tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply }; board.updateMove(m, playerId); } /** @} */ /** @name Player main application interface */ /** @{ */ /** * Player's move. * * A player first throws a dice to get a random direction. Then checks if the direction * is walkable. If it is, then goes to that tile and update player's data. * If the player is a champion then he also picks up a possible supply from the tile. * * @param id The id of the starting tile. * @return An array containing player's final position and possible supply of that position. * The array format is: *
    *
  • 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 dice/direction of the move. *
*/ int[] move(int id) { // Initialize return array with the current data int[] ret = new int[MOVE_DATA_SIZE]; ret[MOVE_TILE_ID] = id; ret[MOVE_ROW] = Position.toRow(id); ret[MOVE_COLUMN] = Position.toCol(id); ret[MOVE_DICE] = Direction.NONE; int supplyFlag =0, moveFlag =0; int diceDirection; do diceDirection = board.dice(); // throw the dice while (!board.isWalkable(id, diceDirection)); moveFlag =1; // mark the successful move // Get next tile Position next = new Position(Position.toRow(id), Position.toCol(id), diceDirection); ret[MOVE_TILE_ID] = next.getId(); // Update move's return data ret[MOVE_ROW] = y = next.getRow(); ret[MOVE_COLUMN] = x = next.getCol(); ret[MOVE_DICE] = diceDirection; // In case of a champion player, try also to pick a supply if (champion && (board.tryPickSupply(next.getId()) != Const.noSupply)) { supplyFlag =1; // mark the successful supply pickup ++score; // keep score } ++dirCounter[diceDirection]; // update direction counters board.updateMove(ret, playerId); // update path Integer[] p = { ret[MOVE_TILE_ID], diceDirection, moveFlag, supplyFlag, dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], Const.noSupply, Const.noOpponent }; path.add(p); return ret; } /** * Prints round information for the player */ void statistics() { if (!path.isEmpty()) { Integer[] last = path.get(path.size()-1); String who = String.format("%12s", name); System.out.print(who + ": score[" + score + "]" + ", dice =" + last[1] + ", tileId =" + last[0] + " (" + Position.toRow(last[0]) + ", " + Position.toCol(last[0]) + ")"); if (last[2] == 0) System.out.println(" *Can not move."); else if (last[3] != 0) System.out.println(" *Found a supply."); else System.out.println(""); } } /** * Prints final statistics for the player * @note * We add this final_statistics() wrapper in order to provide polymorphism to * Player class hierarchy and to be sure that we are not braking the * Liskov substitution principle * @see https://en.wikipedia.org/wiki/Liskov_substitution_principle */ void final_statistics () { } /** Utility to access player's tileID */ int playerTileId() { return Position.toID(y, x); } /** Utility to access player's row position (row coordinate) */ int playerRow() { return y; } /** Utility to access player's column position (column coordinate) */ int playerCol() { return x; } /** @} */ /** * @name Accessor/Mutator interface * @note * Please consider not to use mutator interface. Its the abstraction killer :( * We have added a bit of logic however, in order to make it a bit more safe. */ /** @{ */ int getPlayerId () { return playerId; } String getName() { return name; } Board getBoard () { return board; } int getScore () { return score; } int getX() { return x; } int getY() { return y; } boolean getChampion(){ return champion; } int[] getDirCounter(){ return dirCounter; } ArrayList getPath() { return path; } void setPlayerId(int id) { playerId = id; } void setName(String name) { this.name = name; } void setBoard (Board board){ this.board = board; } void setScore(int score) { this.score = score; } void setX(int x) { assert (x >= 0 && x< Session.boardSize) : "X(column) coordinate must be in the range [0, Session.boardSize)"; this.x = x; } void setY(int y) { assert (y >= 0 && y< Session.boardSize) : "Y(row) coordinate must be in the range [0, Session.boardSize)"; this.y = y; } void setChampion (boolean champion) { this.champion = champion; } public void setDirCounter(int[] dirCounter) { this.dirCounter = dirCounter; } public void setPath(ArrayList path) { this.path = path; } /** @} */ /** @name Class data */ /** @{ */ 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 */ protected int dirCounter[]; protected ArrayList path; /**< * our history. The integer[] format is: *
    *
  • Integer[0]: tileId - The tile id we choose for the move *
  • Integer[1]: dice - The dice (a.k.a direction) of move *
  • Integer[2]: moveStatus - True if it was successful (we can move in that direction) *
  • Integer[3]: tookSupply - True if we took supply *
  • Integer[4]: upCounter - Accumulator to count all the up moves *
  • Integer[5]: righrCounter - Accumulator to count all the right moves *
  • Integer[6]: downCounter - Accumulator to count all the down moves *
  • Integer[7]: leftCounter - Accumulator to count all the left moves *
  • Integer[8]: SupDistance - The distance of the nearest supply (only for heuristic players) *
  • Integer[9]: OppDistance - The distance of the nearest opponent (only for heuristic players) *
* } */ /** @} */ }