@@ -13,7 +13,6 @@ | |||||
package host.labyrinth; | package host.labyrinth; | ||||
import java.util.ArrayList; | import java.util.ArrayList; | ||||
import java.util.Arrays; | |||||
import java.util.function.IntFunction; | import java.util.function.IntFunction; | ||||
/** | /** | ||||
@@ -35,9 +34,10 @@ class Board { | |||||
this.S = 0; | this.S = 0; | ||||
this.W = 0; | this.W = 0; | ||||
tiles = null; | tiles = null; | ||||
supplies =null; | |||||
supplies = null; | |||||
walls = new ArrayList<Edge>(); | walls = new ArrayList<Edge>(); | ||||
moves = new ArrayList<Integer[]>(); | |||||
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE]; | |||||
playerCount =0; | |||||
} | } | ||||
/** | /** | ||||
@@ -54,7 +54,8 @@ class Board { | |||||
tiles = new Tile[N*N]; | tiles = new Tile[N*N]; | ||||
supplies = new Supply[S]; | supplies = new Supply[S]; | ||||
walls = new ArrayList<Edge>(); | walls = new ArrayList<Edge>(); | ||||
moves = new ArrayList<Integer[]>(); | |||||
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE]; | |||||
playerCount =0; | |||||
} | } | ||||
/** | /** | ||||
@@ -72,17 +73,28 @@ class Board { | |||||
*/ | */ | ||||
Board(Board b) { | Board(Board b) { | ||||
// Copy primitives | // Copy primitives | ||||
this.N = b.N; | |||||
this.S = b.S; | |||||
this.W = b.W; | |||||
// Clone arrays | |||||
this.tiles = b.tiles.clone(); | |||||
this.supplies = b.supplies.clone(); | |||||
this.N = b.N; | |||||
this.S = b.S; | |||||
this.W = b.W; | |||||
tiles = new Tile[b.tiles.length]; | |||||
supplies = new Supply[b.supplies.length]; | |||||
walls = new ArrayList<Edge>(); | |||||
moves = new int[Const.numOfPlayers][Player.MOVE_DATA_SIZE]; | |||||
playerCount =b.playerCount; | |||||
// clone moves array of array of primitives | |||||
for (int i=0 ; i<b.moves.length ; ++i) | |||||
this.moves[i] = b.moves[i].clone(); | |||||
// Clone arrays of objects | |||||
for (int i=0 ; i<b.tiles.length ; ++i) | |||||
this.tiles[i] = new Tile(b.tiles[i]); | |||||
for (int i=0 ; i<b.supplies.length ; ++i) | |||||
this.supplies[i] = new Supply(b.supplies[i]); | |||||
// clone vectors | |||||
for (Edge it: b.walls) | for (Edge it: b.walls) | ||||
this.walls.add(new Edge(it)); | |||||
for (Integer[] m : b.moves) { | |||||
this.moves.add(m); | |||||
} | |||||
this.walls.add(new Edge(it)); | |||||
} | } | ||||
/** @} */ | /** @} */ | ||||
@@ -220,29 +232,34 @@ class Board { | |||||
int size () { return N; } | int size () { return N; } | ||||
/** | /** | ||||
* Boards utility to give access to other player moves. | |||||
* Utility function to create player IDs | |||||
* @return The generated player id. | |||||
*/ | |||||
int generatePlayerId () throws Exception { | |||||
if (playerCount < Const.numOfPlayers) | |||||
return playerCount++; | |||||
else | |||||
throw new Exception("Maximum number of players exceeded"); | |||||
} | |||||
/** | |||||
* Boards utility to give access to other player Id. | |||||
* | * | ||||
* @param playerId The id of player who asks | * @param playerId The id of player who asks | ||||
* @return The moves data of all other players | |||||
*/ | |||||
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; | |||||
* @return The other player's Id. | |||||
*/ | |||||
int getOpponentId(int playerId) { | |||||
return Const.numOfPlayers - (playerId +1); | |||||
} | } | ||||
/** | /** | ||||
* Utility function to create player IDs | |||||
* @return The generated player id. | |||||
* Boards utility to give access to other player moves. | |||||
* | |||||
* @param playerId The id of player who asks | |||||
* @return The moves data of other player | |||||
*/ | */ | ||||
int generatePlayerId () { | |||||
moves.add(null); | |||||
return moves.size() -1; | |||||
int[] getOpponentMove (int playerId) { | |||||
return moves[getOpponentId(playerId)]; | |||||
} | } | ||||
/** | /** | ||||
@@ -256,7 +273,8 @@ class Board { | |||||
* @param playerId The id of the player who update his/her data. | * @param playerId The id of the player who update his/her data. | ||||
*/ | */ | ||||
void updateMove(int[] m, int playerId) { | void updateMove(int[] m, int playerId) { | ||||
moves.set(playerId, Arrays.stream(m).boxed().toArray(Integer[]::new)); | |||||
//moves.set(playerId, Arrays.stream(m).boxed().toArray(Integer[]::new)); | |||||
moves[playerId] = m; | |||||
} | } | ||||
/** @} */ | /** @} */ | ||||
@@ -295,7 +313,7 @@ class Board { | |||||
* <a href="https://github.com/sean-parent/sean-parent.github.io/blob/master/better-code/03-data-structures.md"> see also here</a> | * <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. | * @return Reference to inner walls array. | ||||
*/ | */ | ||||
ArrayList<Integer[]> getMoves() { return moves; } | |||||
int[][] getMoves() { return moves; } | |||||
void setN(int N) { this.N = N; } | void setN(int N) { this.N = N; } | ||||
void setS(int S) { this.S = S; } | void setS(int S) { this.S = S; } | ||||
@@ -326,7 +344,7 @@ class Board { | |||||
* @note Use with care. | * @note Use with care. | ||||
* Any call to this function will probably add memory for the garbage collector. | * Any call to this function will probably add memory for the garbage collector. | ||||
*/ | */ | ||||
void setMoves(ArrayList<Integer[]> moves) { this.moves =moves; } | |||||
void setMoves(int[][] moves) { this.moves =moves; } | |||||
/** @} */ | /** @} */ | ||||
@@ -633,6 +651,7 @@ class Board { | |||||
* Array to hold all the walls using the edge representation | * Array to hold all the walls using the edge representation | ||||
* required by the closed room preventing algorithm. | * required by the closed room preventing algorithm. | ||||
*/ | */ | ||||
private ArrayList<Integer[]> moves; | |||||
private int[][] moves; | |||||
private int playerCount; | |||||
/** @} */ | /** @} */ | ||||
} | } |
@@ -19,16 +19,23 @@ import java.util.Collections; | |||||
* Class to hold constant values for entire application | * Class to hold constant values for entire application | ||||
*/ | */ | ||||
class Const { | 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 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 noSupply =-1; /**< Number to indicate the absent of supply */ | ||||
static final int noOpponent =-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 noTileId =-1; /**< Number to indicate wrong tileId */ | ||||
static final int EOR =-1; /**< Number to indicate the End Of Range */ | 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 int viewDistance =3; /**< The max distance of the Heuristic player's ability to see */ | ||||
static final int noView = viewDistance+1; | |||||
static final double opponentFactor =1.0; | |||||
static final double supplyFactor =0.65; | |||||
/** 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. | * Application wide object to hold settings like values for the session. | ||||
@@ -68,13 +75,16 @@ class Direction { | |||||
static final int RIGHT =3; /**< East direction */ | static final int RIGHT =3; /**< East direction */ | ||||
static final int DOWN =5; /**< South direction */ | static final int DOWN =5; /**< South direction */ | ||||
static final int LEFT =7; /**< West direction */ | static final int LEFT =7; /**< West direction */ | ||||
static final int NONE =8; /**< No direction */ | |||||
/** | /** | ||||
* Utility to get the opposite direction. | * Utility to get the opposite direction. | ||||
* @param direction Input direction | * @param direction Input direction | ||||
* @return The opposite direction | * @return The opposite direction | ||||
*/ | */ | ||||
static int opposite (int direction) { return (direction+4)%DirRange.End; } | |||||
static int opposite (int direction) { | |||||
return (direction != NONE) ? (direction+4)%DirRange.End : NONE; | |||||
} | |||||
static int get (int fromId, int toId) { | static int get (int fromId, int toId) { | ||||
if (Position.toID(Position.toRow(fromId), Position.toCol(fromId)-1) == toId) | if (Position.toID(Position.toRow(fromId), Position.toCol(fromId)-1) == toId) | ||||
@@ -133,6 +143,7 @@ class Position { | |||||
case Direction.DOWN: 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.LEFT: this.id = toID(row, col-1); break; | ||||
case Direction.RIGHT: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; | |||||
} | } | ||||
} | } | ||||
@@ -18,13 +18,14 @@ | |||||
* 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 2nd assignment we deal with the creation of a new heuristic player | |||||
* In this 3rd assignment we deal with the creation of a new minimax player | |||||
* who can cheat and manipulate the dice. Documented classes: | * who can cheat and manipulate the dice. Documented classes: | ||||
* - Tile | * - Tile | ||||
* - Supply | * - Supply | ||||
* - Board | * - Board | ||||
* - Player | * - Player | ||||
* - HeuristicPlayer | * - HeuristicPlayer | ||||
* - MinMaxPlayer | |||||
* - 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: | ||||
@@ -156,9 +157,9 @@ public class Game { | |||||
// Create a game, a board and 2 players. | // Create a game, a board and 2 players. | ||||
Game game = new Game(); | Game game = new Game(); | ||||
Board board = new Board(Session.boardSize, Session.supplySize); | Board board = new Board(Session.boardSize, Session.supplySize); | ||||
Player T = new HeuristicPlayer("Theseus", true, board, 0); | |||||
Player T = new MinMaxPlayer("Theseus", true, board, 0); | |||||
Player M = new Player("Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2)); | Player M = new Player("Minotaur", false, board, Position.toID(Session.boardSize/2, Session.boardSize/2)); | ||||
Player players [] = {T, M}; | |||||
Player players [] = {M, T}; | |||||
// Populate data to the board | // Populate data to the board | ||||
board.createBoard(T.playerTileId(), M.playerTileId()); | board.createBoard(T.playerTileId(), M.playerTileId()); | ||||
@@ -1,5 +1,5 @@ | |||||
/** | /** | ||||
* @file Player.java | |||||
* @file HeuristicPlayer.java | |||||
* | * | ||||
* @author | * @author | ||||
* Anastasia Foti AEM:8959 | * Anastasia Foti AEM:8959 | ||||
@@ -29,7 +29,7 @@ class HeuristicPlayer extends Player { | |||||
* @param row The row coordinate of initial player position | * @param row The row coordinate of initial player position | ||||
* @param column The column coordinate of initial player's 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) throws Exception { | |||||
super(name, champion, board, row, column); | super(name, champion, board, row, column); | ||||
} | } | ||||
@@ -40,7 +40,7 @@ class HeuristicPlayer extends Player { | |||||
* @param board Reference to the board of the game | * @param board Reference to the board of the game | ||||
* @param tileId The tileId coordinate of player's initial position | * @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) throws Exception { | |||||
super(name, champion, board, tileId); | super(name, champion, board, tileId); | ||||
} | } | ||||
/** @} */ | /** @} */ | ||||
@@ -58,7 +58,7 @@ class HeuristicPlayer extends Player { | |||||
Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | ||||
for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) { | 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); | |||||
pos = new Position(pos.getRow(), pos.getCol(), direction); | |||||
if (board.hasSupply(pos.getId())) | if (board.hasSupply(pos.getId())) | ||||
return i+1; | return i+1; | ||||
} | } | ||||
@@ -73,14 +73,12 @@ class HeuristicPlayer extends Player { | |||||
*/ | */ | ||||
int opponetInDirection(int currentPos, int direction) { | int opponetInDirection(int currentPos, int direction) { | ||||
Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | ||||
int [][] opps = board.getOpponentMoves(playerId); | |||||
int [] opp = board.getOpponentMove(playerId); | |||||
for (int i=0 ; board.isWalkable(pos.getId(), direction) && i<Const.viewDistance ; ++i) { | 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; | |||||
} | |||||
pos = new Position(pos.getRow(), pos.getCol(), direction); | |||||
if (opp[MOVE_TILE_ID] == pos.getId()) | |||||
return i+1; | |||||
} | } | ||||
return Const.noOpponent; | return Const.noOpponent; | ||||
} | } | ||||
@@ -102,8 +100,15 @@ class HeuristicPlayer extends Player { | |||||
- ((opDist != 0) ? (1.0/opDist * Const.opponentFactor) : 0); | - ((opDist != 0) ? (1.0/opDist * Const.opponentFactor) : 0); | ||||
} | } | ||||
// Must return a new move always | |||||
int getNextMove(int currentPos) { | |||||
/** | |||||
* Selects the best possible move to return | |||||
* @param currentPos Player's current position to the board | |||||
* @return The move array | |||||
* | |||||
* @note | |||||
* This function always return a new move. | |||||
*/ | |||||
int[] getNextMove(int currentPos) { | |||||
Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step); | Range dirs = new Range(DirRange.Begin, DirRange.End, DirRange.Step); | ||||
int N = dirs.size(); | int N = dirs.size(); | ||||
double[] eval = new double[N]; | double[] eval = new double[N]; | ||||
@@ -127,7 +132,8 @@ class HeuristicPlayer extends Player { | |||||
dir = directionOfMax (eval, eval_dir, N); | dir = directionOfMax (eval, eval_dir, N); | ||||
} | } | ||||
Position new_pos = new Position( Position.toRow(currentPos), Position.toCol(currentPos), dir ); | Position new_pos = new Position( Position.toRow(currentPos), Position.toCol(currentPos), dir ); | ||||
return new_pos.getId(); | |||||
int [] ret = {new_pos.getId(), new_pos.getRow(), new_pos.getCol(), dir}; | |||||
return ret; | |||||
} | } | ||||
/** | /** | ||||
@@ -144,37 +150,36 @@ class HeuristicPlayer extends Player { | |||||
* <li> int[0]: The tileId of the final player's position. | * <li> int[0]: The tileId of the final player's position. | ||||
* <li> int[1]: The row 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[2]: The column of the final player's position. | ||||
* <li> int[3]: The supplyId in case player picked one (Const.noSupply otherwise). | |||||
* <li> int[3]: The dice/direction of the move. | |||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
@Override | @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]; | |||||
ret[0] = getNextMove(id); | |||||
ret[1] = y = Position.toRow(ret[0]); | |||||
ret[2] = x = Position.toCol(ret[0]); | |||||
int[] ret = getNextMove(id); | |||||
y = Position.toRow(ret[MOVE_TILE_ID]); | |||||
x = Position.toCol(ret[MOVE_TILE_ID]); | |||||
int supplyFlag =0, moveFlag =1; | 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 && (board.tryPickSupply(ret[MOVE_TILE_ID]) != Const.noSupply)) { | |||||
++score; // keep score | ++score; // keep score | ||||
++supplyFlag; | ++supplyFlag; | ||||
} | } | ||||
int dir = Direction.get(id, ret[0]); // update direction counters | |||||
++dirCounter[dir]; | |||||
++dirCounter[ret[MOVE_DICE]]; // update direction counters | |||||
board.updateMove(ret, playerId); | board.updateMove(ret, playerId); | ||||
// Update supply and opponent distance | // Update supply and opponent distance | ||||
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 o = opponetInDirection(ret[0], d); | |||||
int s = supplyInDirection (ret[MOVE_TILE_ID], d); | |||||
int o = opponetInDirection(ret[MOVE_TILE_ID], d); | |||||
if (s >= 0 && s < smin) smin = s; | if (s >= 0 && s < smin) smin = s; | ||||
if (o >= 0 && o < omin) omin = o; | if (o >= 0 && o < omin) omin = o; | ||||
} | } | ||||
// update path | // update path | ||||
Integer[] p = { | Integer[] p = { | ||||
ret[0], dir, moveFlag, supplyFlag, | |||||
ret[MOVE_TILE_ID], ret[MOVE_DICE], moveFlag, supplyFlag, | |||||
dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], | dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], | ||||
(smin != DirRange.End)? smin:Const.noSupply, (omin != DirRange.End)? omin:Const.noOpponent | (smin != DirRange.End)? smin:Const.noSupply, (omin != DirRange.End)? omin:Const.noOpponent | ||||
}; | }; | ||||
@@ -197,10 +202,10 @@ class HeuristicPlayer extends Player { | |||||
else | else | ||||
System.out.println(""); | System.out.println(""); | ||||
// extra prints for heuristic | // 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"); | |||||
if (last[8] != Const.noSupply) System.out.println(" supply =" + last[8]); | |||||
else System.out.println(" supply = blind"); | |||||
if (last[9] != Const.noOpponent) System.out.println(" opponent =" + last[9]); | |||||
else System.out.println(" opponent = blind"); | |||||
} | } | ||||
} | } | ||||
@@ -0,0 +1,388 @@ | |||||
/** | |||||
* @file MinMaxPlayer.java | |||||
* | |||||
* @author | |||||
* Anastasia Foti AEM:8959 | |||||
* <anastaskf@ece.auth.gr> | |||||
* | |||||
* @author | |||||
* Christos Choutouridis AEM:8997 | |||||
* <cchoutou@ece.auth.gr> | |||||
*/ | |||||
package host.labyrinth; | |||||
/** | |||||
* @brief | |||||
* This class represents the game's minimax player. | |||||
*/ | |||||
class MinMaxPlayer extends Player { | |||||
/** @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 MinMaxPlayer(String name, boolean champion, Board board, int row, int column) throws Exception { | |||||
super(name, champion, board, row, column); | |||||
} | |||||
/** | |||||
* 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 MinMaxPlayer(String name, boolean champion, Board board, int tileId) throws Exception { | |||||
super(name, champion, board, tileId); | |||||
} | |||||
/** @} */ | |||||
/** @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 | |||||
* @param board Reference to the Board object to use | |||||
* | |||||
* @return The distance or Const.noView | |||||
*/ | |||||
int supplyInDirection(int currentPos, int direction, Board board) { | |||||
Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | |||||
for (int i=0 ; i<=Const.viewDistance ; ++i) { | |||||
if (board.hasSupply(pos.getId())) | |||||
return i; | |||||
if (board.isWalkable(pos.getId(), direction)) | |||||
pos = new Position(pos.getRow(), pos.getCol(), direction); | |||||
else | |||||
break; | |||||
} | |||||
return Const.noView; | |||||
} | |||||
/** | |||||
* 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 | |||||
* @param board Reference to the Board object to use | |||||
* | |||||
* @return The distance or Const.noView | |||||
*/ | |||||
int opponetInDirection(int currentPos, int direction, Board board) { | |||||
Position pos = new Position(Position.toRow(currentPos), Position.toCol(currentPos)); | |||||
int[] opp = board.getOpponentMove(playerId); | |||||
for (int i=0 ; i<=Const.viewDistance ; ++i) { | |||||
if (opp[MOVE_TILE_ID] == pos.getId()) | |||||
return i; | |||||
if (board.isWalkable(pos.getId(), direction)) | |||||
pos = new Position(pos.getRow(), pos.getCol(), direction); | |||||
else | |||||
break; | |||||
} | |||||
return Const.noView; | |||||
} | |||||
/** | |||||
* 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 | |||||
* @param board Reference to the Board object to use | |||||
* @return A signed real number. The higher the output, the higher the evaluation. | |||||
*/ | |||||
double evaluate (int currentPos, int direction, Board board) { | |||||
Position next = new Position (Position.toRow(currentPos), Position.toCol(currentPos), direction); | |||||
int preOpDist = opponetInDirection (currentPos, direction, board); | |||||
int preSupDist = supplyInDirection(currentPos, direction, board); | |||||
int postOpDist = opponetInDirection (next.getId(), direction, board); | |||||
int postSupDist = supplyInDirection(next.getId(), direction, board); | |||||
return ((preSupDist != Const.noView) ? Const.preMoveFactor *(1.0/(preSupDist+1) * Const.supplyFactor) : 0) | |||||
- ((preOpDist != Const.noView) ? Const.preMoveFactor *(1.0/(preOpDist+1) * Const.opponentFactor) : 0) | |||||
+ ((postSupDist != Const.noView)? Const.postMoveFactor*(1.0/(preSupDist+1) * Const.supplyFactor) : 0) | |||||
- ((postOpDist != Const.noView) ? Const.postMoveFactor*(1.0/(preOpDist+1) * Const.opponentFactor) : 0); | |||||
} | |||||
/** | |||||
* Executes the minimax algorithm and return a reference to selected move | |||||
* @param node The root node to start | |||||
* @return Reference to the selected move | |||||
*/ | |||||
Node chooseMinMaxMove(Node node) { | |||||
node.setNodeEvaluation(maxValue(node)); | |||||
return node.getPath(); | |||||
} | |||||
/** | |||||
* Selects the best possible move to return | |||||
* @param currentPos Player's current position to the board | |||||
* @return The move array | |||||
* | |||||
* @note | |||||
* This function always return a new move. | |||||
*/ | |||||
int[] getNextMove(int currentPos) { | |||||
Node root = new Node (board); | |||||
int [] opp = board.getOpponentMove(playerId); | |||||
createMySubtree(currentPos, opp[MOVE_TILE_ID], root, root.getNodeDepth()+1); | |||||
return chooseMinMaxMove(root).getNodeMove(); | |||||
} | |||||
/** | |||||
* MinMaxPlayer'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 dice/direction of the move. | |||||
* </ul> | |||||
*/ | |||||
@Override | |||||
int[] move(int id) { | |||||
// Initialize return array with the current data | |||||
int[] ret = getNextMove(id); | |||||
y = Position.toRow(ret[MOVE_TILE_ID]); | |||||
x = Position.toCol(ret[MOVE_TILE_ID]); | |||||
int supplyFlag =0, moveFlag =1; | |||||
// In case of a champion player, try also to pick a supply | |||||
if (champion && (board.tryPickSupply(ret[MOVE_TILE_ID]) != Const.noSupply)) { | |||||
++score; // keep score | |||||
++supplyFlag; | |||||
} | |||||
++dirCounter[ret[MOVE_DICE]]; // update direction counters | |||||
board.updateMove(ret, playerId); | |||||
// Update supply and opponent distance | |||||
int smin =Const.noView, omin =Const.noView; | |||||
for (int d = DirRange.Begin ; d<DirRange.End ; d += DirRange.Step) { | |||||
int s = supplyInDirection (ret[MOVE_TILE_ID], d, board); | |||||
int o = opponetInDirection(ret[MOVE_TILE_ID], d, board); | |||||
if (s < smin) smin = s; | |||||
if (o < omin) omin = o; | |||||
} | |||||
// update path | |||||
Integer[] p = { | |||||
ret[MOVE_TILE_ID], ret[MOVE_DICE], moveFlag, supplyFlag, | |||||
dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], | |||||
smin, omin | |||||
}; | |||||
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(""); | |||||
// extra prints for minimax player | |||||
if (last[8] != Const.noView) System.out.println(" supply =" + last[8]); | |||||
else System.out.println(" supply = blind"); | |||||
if (last[9] != Const.noView) System.out.println(" opponent =" + last[9]); | |||||
else System.out.println(" opponent = 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]); | |||||
} | |||||
/** @} */ | |||||
/** @name Minimax algorithm related part */ | |||||
/** @{ */ | |||||
/** | |||||
* Get the previous direction of the player | |||||
* @param parent Reference to previous nNode | |||||
* @return The previous direction if exist, else return Direction.NONE | |||||
*/ | |||||
private int prevDirection(Node parent) { | |||||
if (parent != null && parent.getParent() != null) | |||||
return parent.getParent().getNodeMove()[MOVE_DICE]; | |||||
return Direction.NONE; | |||||
} | |||||
/** | |||||
* A simulated move in a copy of the bard. | |||||
* | |||||
* @param board The board on witch we simulate the move | |||||
* @param currentPos The current position of the player to the @c board | |||||
* @param dir The direction of the move | |||||
* @param champion Flag to indicate if the player is champion or not | |||||
* @return The move array | |||||
*/ | |||||
private int[] dryMove (Board board, int currentPos, int dir, boolean champion) { | |||||
int[] ret = new int[MOVE_DATA_SIZE]; | |||||
Position p = new Position(Position.toRow(currentPos), Position.toCol(currentPos), dir); | |||||
ret[MOVE_TILE_ID] = p.getId(); | |||||
ret[MOVE_ROW] = p.getRow(); | |||||
ret[MOVE_COLUMN] = p.getCol(); | |||||
ret[MOVE_DICE] = dir; | |||||
board.updateMove(ret, (champion) ? playerId : board.getOpponentId(playerId)); | |||||
return ret; | |||||
} | |||||
/** | |||||
* One of the 2 recursive functions for creating the minimax tree. This one | |||||
* creates children for the MinMax player. | |||||
* | |||||
* @param parent The parent Node | |||||
* @param depth The current depth for the children | |||||
* @param currentPos The tile of MinMax player | |||||
* @param oppCurrentPos The tile of the opponent | |||||
* | |||||
* @note | |||||
* Even though unnecessary we calculate the evaluation for every node and not only for the leafs. | |||||
* This follows the exercise instructions. We could also rely on lazy evaluation of "evaluation()" | |||||
* and use AB pruning but the depth of the tree is not worth the try. | |||||
*/ | |||||
private void createMySubtree (int currentPos, int oppCurrentPos, Node parent, int depth) { | |||||
ShuffledRange dirs = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step); | |||||
int [] nodeMove; | |||||
for (int dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get()) { | |||||
if ((dir != Direction.opposite(prevDirection(parent))) | |||||
&& parent.getNodeBoard().isWalkable(currentPos, dir)) { | |||||
Board nodeBoard = new Board (parent.getNodeBoard()); // clone board | |||||
double eval = evaluate (currentPos, dir, nodeBoard); // evaluate the move | |||||
nodeMove = dryMove (nodeBoard, currentPos, dir, true); // simulate the move | |||||
// make child Node | |||||
Node child = new Node (parent, depth, nodeMove, nodeBoard, eval); | |||||
parent.addChild(child); // add child to tree | |||||
createOppSubtree (nodeMove[MOVE_TILE_ID], oppCurrentPos, child, depth+1); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* One of the 2 recursive functions for creating the minimax tree. This one | |||||
* creates children for the opponent player. | |||||
* | |||||
* @param parent The parent Node | |||||
* @param depth The current depth for the children | |||||
* @param currentPos The tile of MinMax player | |||||
* @param oppCurrentPos The tile of the opponent | |||||
* | |||||
* @note | |||||
* Even though unnecessary we calculate the evaluation for every node and not only for the leafs. | |||||
* This follows the exercise instructions. We could also rely on lazy evaluation of "evaluation()" | |||||
* and use AB pruning but the depth of the tree is not worth the try. | |||||
*/ | |||||
private void createOppSubtree (int currentPos, int oppCurrentPos, Node parent, int depth) { | |||||
ShuffledRange dirs = new ShuffledRange(DirRange.Begin, DirRange.End, DirRange.Step); | |||||
int [] nodeMove; | |||||
for (int dir = dirs.get() ; dir != Const.EOR ; dir = dirs.get()) { | |||||
if ((dir != Direction.opposite(prevDirection(parent))) | |||||
&& parent.getNodeBoard().isWalkable(oppCurrentPos, dir)) { | |||||
Board nodeBoard = new Board(parent.getNodeBoard()); // clone board | |||||
nodeMove = dryMove (nodeBoard, oppCurrentPos, dir, false); // simulate move | |||||
Position init = new Position( // evaluate from "My" perspective the move | |||||
parent.getNodeMove()[MOVE_ROW], | |||||
parent.getNodeMove()[MOVE_COLUMN], | |||||
Direction.opposite(parent.getNodeMove()[MOVE_DICE]) | |||||
); | |||||
double eval = evaluate(init.getId(), parent.getNodeMove()[MOVE_DICE], nodeBoard); | |||||
// make child Node | |||||
Node child = new Node (parent, depth, nodeMove, nodeBoard, eval); | |||||
parent.addChild(child); // add child to tree | |||||
if (depth < Const.minimaxTreeDepth) { | |||||
createMySubtree (currentPos, nodeMove[MOVE_TILE_ID], child, depth+1); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* The Minimax recursive function for the maximizing part. | |||||
* | |||||
* @param node The current Node | |||||
* @return The selected Node | |||||
*/ | |||||
private double maxValue (Node node) { | |||||
if (node.getChildren() == null) { | |||||
node.setPath(node); | |||||
return node.getNodeEvaluation(); | |||||
} | |||||
else { | |||||
double M = Double.NEGATIVE_INFINITY; | |||||
for (Node n : node.getChildren()) { | |||||
n.setNodeEvaluation(minValue(n)); // evaluation propagation | |||||
if (M < n.getNodeEvaluation()) { | |||||
M = n.getNodeEvaluation(); | |||||
node.setPath(n); // path propagation | |||||
} | |||||
} | |||||
return M; | |||||
} | |||||
} | |||||
/** | |||||
* The Minimax recursive function for the minimizing part. | |||||
* | |||||
* @param node The current Node | |||||
* @return The selected Node | |||||
*/ | |||||
private double minValue (Node node) { | |||||
if (node.getChildren() == null) { | |||||
node.setPath(node); | |||||
return node.getNodeEvaluation(); | |||||
} | |||||
else { | |||||
double m = Double.POSITIVE_INFINITY; | |||||
for (Node n : node.getChildren()) { | |||||
n.setNodeEvaluation(maxValue(n)); // evaluation propagation | |||||
if (m > n.getNodeEvaluation()) { | |||||
m = n.getNodeEvaluation(); | |||||
node.setPath(n); // path propagation | |||||
} | |||||
} | |||||
return m; | |||||
} | |||||
} | |||||
/** @} */ | |||||
} |
@@ -0,0 +1,119 @@ | |||||
/** | |||||
* @file Node.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; | |||||
/** | |||||
* Node object for minimax tree. | |||||
*/ | |||||
class Node { | |||||
/** @name Constructors */ | |||||
/** @{ */ | |||||
/** Null initialize constructor */ | |||||
Node () { } | |||||
/** The main constructor for the Node */ | |||||
Node (Node parent, int nodeDepth, int [] nodeMove, Board nodeBoard, double nodeEvaluation) { | |||||
this.parent = parent; | |||||
this.children = null; | |||||
this.nodeDepth = nodeDepth; | |||||
this.nodeMove = nodeMove; | |||||
this.nodeBoard = nodeBoard; | |||||
this.nodeEvaluation = nodeEvaluation; | |||||
this.path = null; | |||||
} | |||||
/** A special constructor for creating a root Node */ | |||||
Node (Board nodeBoard) { | |||||
this.parent = null; | |||||
this.children = null; | |||||
this.nodeDepth = 0; | |||||
this.nodeMove = new int [4]; | |||||
this.nodeBoard = nodeBoard; | |||||
this.nodeEvaluation = 0; | |||||
this.path = null; | |||||
} | |||||
/**@} */ | |||||
/** @name Get/Set interface */ | |||||
/** @{ */ | |||||
/** Get parent */ | |||||
Node getParent() { return parent; } | |||||
/** get children */ | |||||
ArrayList<Node> | |||||
getChildren() { return children; } | |||||
/** get nodeDepth */ | |||||
int getNodeDepth() { return nodeDepth; } | |||||
/** get nodeMove */ | |||||
int[] getNodeMove() { return nodeMove; } | |||||
/** get nodeBoard */ | |||||
Board getNodeBoard() { return nodeBoard; } | |||||
/** get nodeEvluation */ | |||||
double getNodeEvaluation (){ return nodeEvaluation; } | |||||
/** get path */ | |||||
Node getPath() { return path; } | |||||
/** set parent */ | |||||
void setParent(Node parent) { this.parent = parent; } | |||||
/** set children */ | |||||
void setChildren(ArrayList<Node> children) { | |||||
this.children = children; | |||||
} | |||||
/** set nodeDepth */ | |||||
void setNodeDepth(int nodeDepth) { | |||||
this.nodeDepth = nodeDepth; | |||||
} | |||||
/** set nodeMove */ | |||||
void setNodeMove(int[] nodeMove) { | |||||
this.nodeMove = nodeMove; | |||||
} | |||||
/** set nodeBoard */ | |||||
void setNodeBoard(Board nodeBoard) { | |||||
this.nodeBoard = nodeBoard; | |||||
} | |||||
/** set nodeEvaluation */ | |||||
void setNodeEvaluation(double nodeEvaluation) { | |||||
this.nodeEvaluation = nodeEvaluation; | |||||
} | |||||
/** set path */ | |||||
void setPath (Node path) { | |||||
this.path = path; | |||||
} | |||||
/**@}*/ | |||||
/** @name Public API */ | |||||
/** @{ */ | |||||
/** | |||||
* Add a child to the tree | |||||
* @param child The child to add | |||||
* @return the status of the operation | |||||
*/ | |||||
boolean addChild (Node child) { | |||||
if (children == null) | |||||
children = new ArrayList<>(); | |||||
return children.add(child); | |||||
} | |||||
/**@}*/ | |||||
/** @name Data members */ | |||||
/** @{ */ | |||||
private Node parent; /**< Back reference to parent Node */ | |||||
private ArrayList<Node> children; /**< Fwd reference to leaf Nodes */ | |||||
private int nodeDepth; /**< The Node's depth */ | |||||
private int[] nodeMove; /**< The Node's move data [tile, initTile, points, roll]*/ | |||||
private Board nodeBoard; /**< Reference to Board's copy of the current node*/ | |||||
private double nodeEvaluation; /**< The Node's evaluation result */ | |||||
private Node path; /**< The minimax evaluation path */ | |||||
/**@}*/ | |||||
} |
@@ -19,6 +19,13 @@ import java.util.ArrayList; | |||||
* This class represents the game's player | * This class represents the game's player | ||||
*/ | */ | ||||
class 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 */ | /** @name Constructors */ | ||||
/** @{ */ | /** @{ */ | ||||
@@ -30,7 +37,7 @@ class Player { | |||||
* @param row The row coordinate of initial player position | * @param row The row coordinate of initial player position | ||||
* @param column The column coordinate of initial player's position | * @param column The column coordinate of initial player's position | ||||
*/ | */ | ||||
Player(String name, boolean champion, Board board, int row, int column) { | |||||
Player(String name, boolean champion, Board board, int row, int column) throws Exception { | |||||
this.playerId = board.generatePlayerId(); | this.playerId = board.generatePlayerId(); | ||||
this.name = name; | this.name = name; | ||||
this.board = board; | this.board = board; | ||||
@@ -38,7 +45,7 @@ class Player { | |||||
this.x = column; | this.x = column; | ||||
this.y = row; | this.y = row; | ||||
this.champion = champion; | this.champion = champion; | ||||
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is worst. | |||||
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is the worst. | |||||
this.path = new ArrayList<Integer[]>(); | this.path = new ArrayList<Integer[]>(); | ||||
int[] m = { | int[] m = { | ||||
Position.toID(row, column), row, column, Const.noSupply | Position.toID(row, column), row, column, Const.noSupply | ||||
@@ -53,7 +60,7 @@ class Player { | |||||
* @param board Reference to the board of the game | * @param board Reference to the board of the game | ||||
* @param tileId The tileId coordinate of player's initial position | * @param tileId The tileId coordinate of player's initial position | ||||
*/ | */ | ||||
Player(String name, boolean champion, Board board, int tileId) { | |||||
Player(String name, boolean champion, Board board, int tileId) throws Exception { | |||||
this.playerId = board.generatePlayerId(); | this.playerId = board.generatePlayerId(); | ||||
this.name = name; | this.name = name; | ||||
this.board = board; | this.board = board; | ||||
@@ -61,7 +68,7 @@ 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; | ||||
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is worst. | |||||
this.dirCounter= new int[DirRange.End]; // yes we spoil some memory. Java is the worst. | |||||
this.path = new ArrayList<Integer[]>(); | this.path = new ArrayList<Integer[]>(); | ||||
int[] m = { | int[] m = { | ||||
tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply | tileId, Position.toRow(tileId), Position.toCol(tileId), Const.noSupply | ||||
@@ -87,37 +94,39 @@ class Player { | |||||
* <li> int[0]: The tileId of the final player's position. | * <li> int[0]: The tileId of the final player's position. | ||||
* <li> int[1]: The row 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[2]: The column of the final player's position. | ||||
* <li> int[1]: The supplyId in case player picked one (Const.noSupply otherwise). | |||||
* <li> int[3]: The dice/direction of the move. | |||||
* </ul> | * </ul> | ||||
*/ | */ | ||||
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]; | |||||
ret[0] = id; | |||||
ret[1] = Position.toRow(id); | |||||
ret[2] = Position.toCol(id); | |||||
ret[3] = Const.noSupply; | |||||
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 supplyFlag =0, moveFlag =0; | ||||
int diceDirection = board.dice(); // throw the dice | |||||
if (board.isWalkable(id, diceDirection)) { // The result is walkable | |||||
moveFlag =1; // mark the successful move | |||||
// Get next tile | |||||
Position next = new Position(Position.toRow(id), Position.toCol(id), diceDirection); | |||||
ret[0] = next.getId(); // Update player's and return data | |||||
ret[1] = y = next.getRow(); | |||||
ret[2] = x = next.getCol(); | |||||
// In case of a champion player, try also to pick a supply | |||||
if (champion && (ret[3] = 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); | |||||
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 | // update path | ||||
Integer[] p = { | Integer[] p = { | ||||
ret[0], diceDirection, moveFlag, supplyFlag, | |||||
ret[MOVE_TILE_ID], diceDirection, moveFlag, supplyFlag, | |||||
dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], | dirCounter[Direction.UP], dirCounter[Direction.RIGHT], dirCounter[Direction.DOWN], dirCounter[Direction.LEFT], | ||||
Const.noSupply, Const.noOpponent | Const.noSupply, Const.noOpponent | ||||
}; | }; | ||||