First draft of 2nd version
This commit is contained in:
parent
c14d1d812f
commit
d581bd6c02
BIN
ds_project_2020-2021_PartB_v3.pdf
Normal file
BIN
ds_project_2020-2021_PartB_v3.pdf
Normal file
Binary file not shown.
@ -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<Edge>();
|
||||
moves = new ArrayList<Integer[]>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -52,6 +54,7 @@ class Board {
|
||||
tiles = new Tile[N*N];
|
||||
supplies = new Supply[S];
|
||||
walls = new ArrayList<Edge>();
|
||||
moves = new ArrayList<Integer[]>();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -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<Edge> getWalls() { return walls; }
|
||||
|
||||
/**
|
||||
* @note Use it with care. Any use of this function results to what Sean Parent calls "incidental data-structure".
|
||||
* <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a>
|
||||
* @return Reference to inner walls array.
|
||||
*/
|
||||
ArrayList<Integer[]> 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<Edge> 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<Integer[]> 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<Integer[]> moves;
|
||||
/** @} */
|
||||
}
|
||||
|
@ -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<Integer> numbers; /**< handle to range */
|
||||
|
@ -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());
|
||||
|
191
src/host/labyrinth/HeuristicPlayer.java
Normal file
191
src/host/labyrinth/HeuristicPlayer.java
Normal file
@ -0,0 +1,191 @@
|
||||
/**
|
||||
* @file Player.java
|
||||
*
|
||||
* @author
|
||||
* Anastasia Foti AEM:8959
|
||||
* <anastaskf@ece.auth.gr>
|
||||
*
|
||||
* @author
|
||||
* Christos Choutouridis AEM:8997
|
||||
* <cchoutou@ece.auth.gr>
|
||||
*/
|
||||
|
||||
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<Integer[]>();
|
||||
}
|
||||
|
||||
public HeuristicPlayer(String name, boolean champion, Board board, int tileId) {
|
||||
super(name, champion, board, tileId);
|
||||
path = new ArrayList<Integer[]>();
|
||||
}
|
||||
/** @} */
|
||||
|
||||
/** @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<Const.viewDistance ; ++i) {
|
||||
pos = new Position(Position.toRow(pos.getId()), Position.toCol(pos.getId()), direction);
|
||||
if (board.hasSupply(pos.getId()))
|
||||
return i+1;
|
||||
}
|
||||
return Const.noSupply;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility to get the distance of a possible opponent in some direction
|
||||
* @param currentPos The current position of the player
|
||||
* @param direction The direction to check
|
||||
* @return The distance or Const.noOpponent
|
||||
*/
|
||||
int opponetInDirection(int currentPos, int direction) {
|
||||
Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos));
|
||||
int [][] opps = board.getOpponentMoves(playerId);
|
||||
|
||||
for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) {
|
||||
pos = new Position(Position.toRow(pos.getId()), Position.toCol(pos.getId()), direction);
|
||||
for (int o =0 ; o<opps.length; ++o) {
|
||||
if (opps[o][0] == pos.getId())
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
return Const.noOpponent;
|
||||
}
|
||||
|
||||
double evaluate (int currentPos, int dice) {
|
||||
int opDist = opponetInDirection (currentPos, dice);
|
||||
int supDist = supplyInDirection(currentPos, dice);
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// Must return a new move always
|
||||
int getNextMove(int currentPos) {
|
||||
Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step);
|
||||
int N = dirs.size();
|
||||
double[] eval = new double[N];
|
||||
int [] eval_dir = new int[N];
|
||||
|
||||
for (int i =0, dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get(), ++i) {
|
||||
if (board.isWalkable(currentPos, dir))
|
||||
eval[i] = evaluate(currentPos, dir);
|
||||
else
|
||||
eval[i] = Double.NEGATIVE_INFINITY;
|
||||
eval_dir[i] = dir;
|
||||
}
|
||||
int dir;
|
||||
if (isUnevaluated(eval, N)) {
|
||||
ShuffledRange r = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step);
|
||||
do
|
||||
dir = r.get();
|
||||
while (!board.isWalkable(currentPos, dir));
|
||||
}
|
||||
else {
|
||||
dir = directionOfMax (eval, eval_dir, N);
|
||||
}
|
||||
Position new_pos = new Position( Position.toRow(currentPos), Position.toCol(currentPos), dir );
|
||||
return new_pos.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* HeuristicPlayer's move.
|
||||
*
|
||||
* A player of this kind cheats. He does not throw a dice to get a direction. In contrary he
|
||||
* calculates his next move very carefully.
|
||||
* 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:
|
||||
* <ul>
|
||||
* <li> int[0]: The tileId of the final player's position.
|
||||
* <li> int[1]: The row of the final player's position.
|
||||
* <li> int[2]: The column of the final player's position.
|
||||
* <li> int[3]: The supplyId in case player picked one (Const.noSupply otherwise).
|
||||
* </ul>
|
||||
*/
|
||||
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<DirRange.End ; d += DirRange.Step) {
|
||||
int s = supplyInDirection (ret[0], d);
|
||||
int o = opponetInDirection(ret[0], d);
|
||||
if (s < smin) smin = s;
|
||||
if (o < omin) omin = o;
|
||||
}
|
||||
Integer[] p = {dir, supplyFlag, smin, omin };
|
||||
|
||||
path.add(p);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** @} */
|
||||
/** @name Class data */
|
||||
/** @{ */
|
||||
private ArrayList<Integer[]> path; /**< our history
|
||||
* The integer[] format is:
|
||||
* { dice, tookSupply, SupplyDistance, OpponentDistance}
|
||||
*/
|
||||
|
||||
/** @} */
|
||||
}
|
@ -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 */
|
||||
/** @} */
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user