Browse Source

DEV: 2nd version of the assignment (the code part)

tags/v2.0b1
parent
commit
a4d5299514
4 changed files with 218 additions and 79 deletions
  1. +1
    -0
      src/host/labyrinth/Common.java
  2. +25
    -21
      src/host/labyrinth/Game.java
  3. +116
    -42
      src/host/labyrinth/HeuristicPlayer.java
  4. +76
    -16
      src/host/labyrinth/Player.java

+ 1
- 0
src/host/labyrinth/Common.java View File

@@ -57,6 +57,7 @@ class DirRange {
static final int Begin =1; /**< Iterator style begin of range direction (starting north) */ 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 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 Step =2; /**< Step for iterator style direction */
static final int numOfDirections =4;
} }


/** /**


+ 25
- 21
src/host/labyrinth/Game.java View File

@@ -18,14 +18,15 @@
* all the supplies of the board before Minotaur catches him and before the * all the supplies of the board before Minotaur catches him and before the
* game ends. * game ends.
* *
* In this first assignment we deal with the board's creation and a basic
* player-game logic. The game is build around a number of classes:
* - \ref Tile
* In this 2nd assignment we deal with the creation of a new heuristic player
* who can cheat and manipulate the dice. Documented classes:
* - Tile
* - Supply * - Supply
* - Board * - Board
* - Player * - Player
* - HeuristicPlayer
* - Game * - Game
*
*
* Which are the requested classes. We also provide some extra functionalities in: * Which are the requested classes. We also provide some extra functionalities in:
* - Const * - Const
* - Session * - Session
@@ -153,10 +154,11 @@ public class Game {
if (!Game.getArguments(args)) throw new Exception(""); if (!Game.getArguments(args)) throw new Exception("");


// Create a game, a board and 2 players. // Create a game, a board and 2 players.
Game game = new Game();
Board board = new Board(Session.boardSize, Session.supplySize);
Player T = new HeuristicPlayer("Theseus", true, board, 0);
Player M = new Player("Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2));
Game game = new Game();
Board board = new Board(Session.boardSize, Session.supplySize);
Player T = new HeuristicPlayer("Theseus", true, board, 0);
Player M = new Player("Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2));
Player players [] = {T, M};


// Populate data to the board // Populate data to the board
board.createBoard(T.playerTileId(), M.playerTileId()); board.createBoard(T.playerTileId(), M.playerTileId());
@@ -169,34 +171,36 @@ public class Game {
game.waitUser (); game.waitUser ();
// Main game loop // Main game loop
while (true) { while (true) {
int[] m;
System.out.println(); System.out.println();
System.out.println("State after round: " + (game.round()+1));
System.out.println("Round: " + (game.round()+1));


// Player moves
m = T.move(T.playerTileId());
System.out.println(T.getName() + ":\t tileId =" + m[0] + " (" + m[1] + ", " + m[2] + ")");
m = M.move(M.playerTileId());
System.out.println(M.getName() + ":\t tileId =" + m[0] + " (" + m[1] + ", " + m[2] + ")");
// Players moves
for (Player p : players) {
p.move(p.playerTileId());
p.statistics();
}
board.printBoard( board.printBoard(
board.getStringRepresentation(T.playerTileId(), M.playerTileId()) board.getStringRepresentation(T.playerTileId(), M.playerTileId())
); );


// Loop termination cases // Loop termination cases
if (T.getScore() == 4) { if (T.getScore() == 4) {
System.out.println(T.getName() + " Wins!!! Score =" + T.getScore());
System.exit(0);
System.out.println("**** " + T.getName() + " Wins!!! Score =" + T.getScore() + " ****");
break;
} }
if (M.getScore() == 4 || M.playerTileId() == T.playerTileId()) { if (M.getScore() == 4 || M.playerTileId() == T.playerTileId()) {
System.out.println(M.getName() + " Wins!!! Score =" + M.getScore());
System.exit(0);
System.out.println("**** " + M.getName() + " Wins!!! Score =" + M.getScore());
break;
} }
if (!(game.nextRound() < Session.maxRounds)) { if (!(game.nextRound() < Session.maxRounds)) {
System.out.println("New day has come... Tie!!!");
System.exit(0);
System.out.println("**** New day has come... Tie! ****");
break;
} }
game.waitUser (); game.waitUser ();
} }
for (Player p : players)
p.final_statistics();
System.exit(0);
} }
catch (Exception e) { catch (Exception e) {
// We don't handle exceptions. Print error and exit with error status. // We don't handle exceptions. Print error and exit with error status.


+ 116
- 42
src/host/labyrinth/HeuristicPlayer.java View File

@@ -12,9 +12,6 @@


package host.labyrinth; package host.labyrinth;


import java.util.ArrayList;
//import java.util.Arrays;

/** /**
* @brief * @brief
* This class represents the game's player who cheats. * This class represents the game's player who cheats.
@@ -23,14 +20,28 @@ class HeuristicPlayer extends Player {


/** @name Constructors */ /** @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
*/
public HeuristicPlayer(String name, boolean champion, Board board, int row, int column) { public HeuristicPlayer(String name, boolean champion, Board board, int row, int column) {
super(name, champion, board, row, column); super(name, champion, board, row, column);
path = new ArrayList<Integer[]>();
} }


/**
* 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
*/
public HeuristicPlayer(String name, boolean champion, Board board, int tileId) { public HeuristicPlayer(String name, boolean champion, Board board, int tileId) {
super(name, champion, board, tileId); super(name, champion, board, tileId);
path = new ArrayList<Integer[]>();
} }
/** @} */ /** @} */


@@ -74,34 +85,21 @@ class HeuristicPlayer extends Player {
return Const.noOpponent; return Const.noOpponent;
} }


double evaluate (int currentPos, int dice) {
int opDist = opponetInDirection (currentPos, dice);
int supDist = supplyInDirection(currentPos, dice);
/**
* This is the main move evaluation function.
* @param currentPos The current position of the player (before the move to evaluate)
* @param direction The direction (a.k.a. the move) to evaluate
* @return A signed real number. The higher the output, the higher the evaluation.
*/
double evaluate (int currentPos, int direction) {
int opDist = opponetInDirection (currentPos, direction);
int supDist = supplyInDirection(currentPos, direction);


// saturate // saturate
opDist = (opDist == Const.noOpponent) ? Integer.MAX_VALUE : opDist;
supDist = (supDist == Const.noSupply) ? Integer.MAX_VALUE : supDist;
return 1.0/supDist * Const.supplyFactor
- 1.0/opDist * Const.opponentFactor;
}

int directionOfMax (double[] eval, int[] eval_dir, int N) {
double M = Double.NEGATIVE_INFINITY;
int M_idx = -1;
for (int i =0; i < N ; ++i) {
if (eval[i] > M) {
M = eval[i];
M_idx = i;
}
}
return eval_dir[M_idx];
}

boolean isUnevaluated (double[] eval, int N) {
for (int i =0 ; i<N ; ++i)
if (eval[i] != 0 && eval[i] != Double.NEGATIVE_INFINITY)
return false;
return true;
opDist = (opDist == Const.noOpponent) ? 0: opDist;
supDist = (supDist == Const.noSupply) ? 0 : supDist;
return ((supDist != 0)? (1.0/supDist * Const.supplyFactor) : 0)
- ((opDist != 0) ? (1.0/opDist * Const.opponentFactor) : 0);
} }


// Must return a new move always // Must return a new move always
@@ -149,43 +147,119 @@ class HeuristicPlayer extends Player {
* <li> int[3]: The supplyId in case player picked one (Const.noSupply otherwise). * <li> int[3]: The supplyId in case player picked one (Const.noSupply otherwise).
* </ul> * </ul>
*/ */
@Override
int[] move(int id) { int[] move(int id) {
// Initialize return array with the current data // Initialize return array with the current data
int[] ret = new int[Const.moveItems]; int[] ret = new int[Const.moveItems];
ret[0] = getNextMove(id); ret[0] = getNextMove(id);
ret[1] = y = Position.toRow(ret[0]); ret[1] = y = Position.toRow(ret[0]);
ret[2] = x = Position.toCol(ret[0]); ret[2] = x = Position.toCol(ret[0]);
int supplyFlag = 0;
int supplyFlag =0, moveFlag =1;
// In case of a champion player, try also to pick a supply // In case of a champion player, try also to pick a supply
if (champion && (ret[3] = board.tryPickSupply(ret[0])) != Const.noSupply) { if (champion && (ret[3] = board.tryPickSupply(ret[0])) != Const.noSupply) {
++score; // keep score ++score; // keep score
++supplyFlag; ++supplyFlag;
System.out.println(name + ":\t*Found a supply. [score: " + score + "]");
} }
int dir = Direction.get(id, ret[0]); // update direction counters
++dirCounter[dir];
board.updateMove(ret, playerId); board.updateMove(ret, playerId);


// Update supply and opponent distance // Update supply and opponent distance
int dir = Direction.get(id, ret[0]);
int smin =DirRange.End, omin =DirRange.End; int smin =DirRange.End, omin =DirRange.End;
for (int d = DirRange.Begin ; d<DirRange.End ; d += DirRange.Step) { for (int d = DirRange.Begin ; d<DirRange.End ; d += DirRange.Step) {
int s = supplyInDirection (ret[0], d); int s = supplyInDirection (ret[0], d);
int o = opponetInDirection(ret[0], d); int o = opponetInDirection(ret[0], d);
if (s < smin) smin = s;
if (o < omin) omin = o;
if (s >= 0 && s < smin) smin = s;
if (o >= 0 && o < omin) omin = o;
} }
Integer[] p = {dir, supplyFlag, smin, omin };

// update path
Integer[] p = {
ret[0], dir, moveFlag, supplyFlag,
dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT],
(smin != DirRange.End)? smin:Const.noSupply, (omin != DirRange.End)? omin:Const.noOpponent
};
path.add(p); path.add(p);
return ret; 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("");
// extra prints for heuristic
if (last[8] != Const.noSupply) System.out.println(" supply distance =" + last[8]);
else System.out.println(" supply distance = blind");
if (last[9] != Const.noOpponent) System.out.println(" opponent distance =" + last[9]);
else System.out.println(" opponent distance = blind");
}
}
/**
* Prints final statistics for the player
*/
void final_statistics () {
String who = String.format("%12s", name);
System.out.println();
System.out.println(who + ": score[" + score + "]");
System.out.println(" Moves up: " + dirCounter[Direction.UP]);
System.out.println(" Moves right: " + dirCounter[Direction.RIGHT]);
System.out.println(" Moves down: " + dirCounter[Direction.DOWN]);
System.out.println(" Moves left: " + dirCounter[Direction.LEFT]);
}

/** @} */ /** @} */

/**
* A small utility to extract the direction of maximum evaluation result.
*
* We search into the \c eval results and find the index of the maximum evaluation.
* Then we return the direction of \c eval_dir matrix at the same index we found.
*
* @param eval Array with evaluation results for each direction
* @param eval_dir Array with the matching direction to \c eval array
* @param N The size of both arrays
* @return The direction of maximum evaluation. If \c eval is empty returns the first item \c eval[0]
* @note
* This function should not be called if there is at least one evaluation result in \c eval
*/
private int directionOfMax (double[] eval, int[] eval_dir, int N) {
double M = Double.NEGATIVE_INFINITY;
int M_idx = 0;
for (int i =0; i < N ; ++i) {
if (eval[i] > M) {
M = eval[i];
M_idx = i;
}
}
return eval_dir[M_idx];
}

/**
* A small utility to check if there is at least one evaluation result in the \c eval array
* @param eval The array to check
* @param N The size of the array
* @return True if there is none, false otherwise
*/
private boolean isUnevaluated (double[] eval, int N) {
for (int i =0 ; i<N ; ++i)
if (eval[i] != 0 && eval[i] != Double.NEGATIVE_INFINITY)
return false;
return true;
}
/** @name Class data */ /** @name Class data */
/** @{ */ /** @{ */
private ArrayList<Integer[]> path; /**< our history
* The integer[] format is:
* { dice, tookSupply, SupplyDistance, OpponentDistance}
*/



/** @} */ /** @} */
} }

+ 76
- 16
src/host/labyrinth/Player.java View File

@@ -12,6 +12,8 @@


package host.labyrinth; package host.labyrinth;


import java.util.ArrayList;

/** /**
* @brief * @brief
* This class represents the game's player * This class represents the game's player
@@ -22,7 +24,6 @@ class Player {


/** /**
* Create a new player and put him at the row-column coordinates * Create a new player and put him at the row-column coordinates
* @param id The id of the player
* @param name The name of the player * @param name The name of the player
* @param champion Flag to indicate if a player is a `champion` * @param champion Flag to indicate if a player is a `champion`
* @param board Reference to the board of the game * @param board Reference to the board of the game
@@ -37,13 +38,16 @@ class Player {
this.x = column; this.x = column;
this.y = row; this.y = row;
this.champion = champion; this.champion = champion;
int[] m = {Position.toID(row, column), row, column, Const.noSupply};
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is worst.
this.path = new ArrayList<Integer[]>();
int[] m = {
Position.toID(row, column), row, column, Const.noSupply
};
board.updateMove(m, playerId); board.updateMove(m, playerId);
} }


/** /**
* Create a new player and put him at the row-column coordinates * Create a new player and put him at the row-column coordinates
* @param id The id of the player
* @param name The name of the player * @param name The name of the player
* @param champion Flag to indicate if a player is a `champion` * @param champion Flag to indicate if a player is a `champion`
* @param board Reference to the board of the game * @param board Reference to the board of the game
@@ -57,7 +61,11 @@ class Player {
this.x = Position.toCol(tileId); this.x = Position.toCol(tileId);
this.y = Position.toRow(tileId); this.y = Position.toRow(tileId);
this.champion = champion; this.champion = champion;
int[] m = {tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply};
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is worst.
this.path = new ArrayList<Integer[]>();
int[] m = {
tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply
};
board.updateMove(m, playerId); board.updateMove(m, playerId);
} }
/** @} */ /** @} */
@@ -89,9 +97,11 @@ class Player {
ret[1] = Position.toRow(id); ret[1] = Position.toRow(id);
ret[2] = Position.toCol(id); ret[2] = Position.toCol(id);
ret[3] = Const.noSupply; ret[3] = Const.noSupply;
int supplyFlag =0, moveFlag =0;


int diceDirection = board.dice(); // throw the dice int diceDirection = board.dice(); // throw the dice
if (board.isWalkable(id, diceDirection)) { // The result is walkable if (board.isWalkable(id, diceDirection)) { // The result is walkable
moveFlag =1; // mark the successful move
// Get next tile // Get next tile
Position next = new Position(Position.toRow(id), Position.toCol(id), diceDirection); Position next = new Position(Position.toRow(id), Position.toCol(id), diceDirection);
ret[0] = next.getId(); // Update player's and return data ret[0] = next.getId(); // Update player's and return data
@@ -99,16 +109,49 @@ class Player {
ret[2] = x = next.getCol(); ret[2] = x = next.getCol();
// In case of a champion player, try also to pick a supply // In case of a champion player, try also to pick a supply
if (champion && (ret[3] = board.tryPickSupply(next.getId())) != Const.noSupply) { if (champion && (ret[3] = board.tryPickSupply(next.getId())) != Const.noSupply) {
supplyFlag =1; // mark the successful supply pickup
++score; // keep score ++score; // keep score
System.out.println(name + ":\t*Found a supply. [score: " + score + "]");
} }
++dirCounter[diceDirection]; // update direction counters
board.updateMove(ret, playerId); board.updateMove(ret, playerId);
} }
else
System.out.println(name + ":\t*Can not move.");
// update path
Integer[] p = {
ret[0], diceDirection, moveFlag, supplyFlag,
dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT],
Const.noSupply, Const.noOpponent
};
path.add(p);
return ret; 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 */ /** Utility to access player's tileID */
int playerTileId() { return Position.toID(y, x); } int playerTileId() { return Position.toID(y, x); }
/** Utility to access player's row position (row coordinate) */ /** Utility to access player's row position (row coordinate) */
@@ -147,17 +190,34 @@ class Player {
void setChampion (boolean champion) { void setChampion (boolean champion) {
this.champion = champion; this.champion = champion;
} }

/** @} */ /** @} */
/** @name Class data */ /** @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 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<Integer[]> path;
/**<
* our history. The integer[] format is:
* <ul>
* <li> Integer[0]: tileId - The tile id we choose for the move
* <li> Integer[1]: dice - The dice (a.k.a direction) of move
* <li> Integer[2]: moveStatus - True if it was successful (we can move in that direction)
* <li> Integer[3]: tookSupply - True if we took supply
* <li> Integer[4]: upCounter - Accumulator to count all the up moves
* <li> Integer[5]: righrCounter - Accumulator to count all the right moves
* <li> Integer[6]: downCounter - Accumulator to count all the down moves
* <li> Integer[7]: leftCounter - Accumulator to count all the left moves
* <li> Integer[8]: SupDistance - The distance of the nearest supply (only for heuristic players)
* <li> Integer[9]: OppDistance - The distance of the nearest opponent (only for heuristic players)
* </ul>
* }
*/
/** @} */ /** @} */
} }

Loading…
Cancel
Save