@@ -5,3 +5,4 @@ | |||||
*.classpath | *.classpath | ||||
/bin/ |
@@ -1,6 +1,7 @@ | |||||
package net.hoo2.auth.dsproject.snake; | package net.hoo2.auth.dsproject.snake; | ||||
import java.lang.Math; | import java.lang.Math; | ||||
import java.util.*; | |||||
/** | /** | ||||
* @class Board | * @class Board | ||||
@@ -33,6 +34,7 @@ public class Board { | |||||
snakes = null; | snakes = null; | ||||
ladders = null; | ladders = null; | ||||
apples = null; | apples = null; | ||||
players = null; | |||||
} | } | ||||
/** | /** | ||||
@@ -64,6 +66,7 @@ public class Board { | |||||
snakes = new Snake[numOfSnakes]; | snakes = new Snake[numOfSnakes]; | ||||
ladders = new Ladder[numOfLadders]; | ladders = new Ladder[numOfLadders]; | ||||
apples = new Apple[numOfApples]; | apples = new Apple[numOfApples]; | ||||
players = null; | |||||
createBoard (); // Complete board preparation and make all the element memory allocations | createBoard (); // Complete board preparation and make all the element memory allocations | ||||
} | } | ||||
@@ -81,6 +84,7 @@ public class Board { | |||||
snakes = new Snake[B.getSnakes().length]; | snakes = new Snake[B.getSnakes().length]; | ||||
ladders = new Ladder[B.getLadders().length]; | ladders = new Ladder[B.getLadders().length]; | ||||
apples = new Apple[B.getApples().length]; | apples = new Apple[B.getApples().length]; | ||||
players = B.getPlayers(); // reference only (don't need to clone) | |||||
// Copy B's guts into new memory | // Copy B's guts into new memory | ||||
copyTiles(B.getTiles()); | copyTiles(B.getTiles()); | ||||
copySnakes(B.getSnakes()); | copySnakes(B.getSnakes()); | ||||
@@ -139,6 +143,13 @@ public class Board { | |||||
*/ | */ | ||||
void setApples(Apple[] apples) { this.apples = apples; } | void setApples(Apple[] apples) { this.apples = apples; } | ||||
/** get reference to players */ | |||||
ArrayList<Player> getPlayers() { return players; } | |||||
/** set reference to players */ | |||||
void setPlayers (ArrayList<Player> players) { | |||||
this.players = players; | |||||
} | |||||
/** | /** | ||||
* Copy tiles | * Copy tiles | ||||
* @param tiles Source of tiles to use | * @param tiles Source of tiles to use | ||||
@@ -552,6 +563,7 @@ public class Board { | |||||
private Snake[] snakes; /**< Board's snakes */ | private Snake[] snakes; /**< Board's snakes */ | ||||
private Ladder[] ladders; /**< Board's ladders */ | private Ladder[] ladders; /**< Board's ladders */ | ||||
private Apple[] apples; /**< Board's apples */ | private Apple[] apples; /**< Board's apples */ | ||||
private ArrayList<Player> players; /**< oard's copy of players reference vector */ | |||||
/** @} */ | /** @} */ | ||||
} | } | ||||
@@ -2,7 +2,7 @@ package net.hoo2.auth.dsproject.snake; | |||||
/** | /** | ||||
* @mainpage | * @mainpage | ||||
* @title Snake game project. -- Part 2 -- | |||||
* @title Snake game project. -- Part 3 -- | |||||
* | * | ||||
* This is the code documentation page of the Snake game project. | * This is the code documentation page of the Snake game project. | ||||
* Listed are: | * Listed are: | ||||
@@ -173,20 +173,35 @@ public class Game { | |||||
boolean registerPlayer (int playerId, String name) { | boolean registerPlayer (int playerId, String name) { | ||||
if (players.size() >= MAX_PLAYERS) | if (players.size() >= MAX_PLAYERS) | ||||
return false; | return false; | ||||
players.add(new Player(playerId, name, board)); | |||||
players.add(new Player(playerId, name)); | |||||
return true; | return true; | ||||
} | } | ||||
// /** | |||||
// * Register a heuristic player to the game | |||||
// * @param playerId The player ID to use | |||||
// * @param name The player name to use | |||||
// * @return The status of the operation | |||||
// */ | |||||
// boolean registerHeuristicPlayer (int playerId, String name) { | |||||
// if (players.size() >= MAX_PLAYERS) | |||||
// return false; | |||||
// //players.add(new HeuristicPlayer(playerId, name, board)); | |||||
// players.add(new HeuristicPlayer(playerId, name)); | |||||
// return true; | |||||
// } | |||||
/** | /** | ||||
* Register a heuristic player to the game | * Register a heuristic player to the game | ||||
* @param playerId The player ID to use | * @param playerId The player ID to use | ||||
* @param name The player name to use | * @param name The player name to use | ||||
* @return The status of the operation | * @return The status of the operation | ||||
*/ | */ | ||||
boolean registerHeuristicPlayer (int playerId, String name) { | |||||
boolean registerMinMaxPlayer (int playerId, String name) { | |||||
if (players.size() >= MAX_PLAYERS) | if (players.size() >= MAX_PLAYERS) | ||||
return false; | return false; | ||||
players.add(new HeuristicPlayer(playerId, name, board)); | |||||
//players.add(new HeuristicPlayer(playerId, name, board)); | |||||
players.add(new MinMaxPlayer(playerId, name)); | |||||
return true; | return true; | ||||
} | } | ||||
@@ -246,7 +261,7 @@ public class Game { | |||||
// using a dice throw | // using a dice throw | ||||
for (Integer pid : playingOrder.keySet()) { | for (Integer pid : playingOrder.keySet()) { | ||||
Player p = _getPlayer(pid); | Player p = _getPlayer(pid); | ||||
tile = p.getNextMove (p.getTile()); | |||||
tile = p.getNextMove (board, p.getTile())[0]; | |||||
p.statistics(verbose, false); | p.statistics(verbose, false); | ||||
if (tile>= board.getN()*board.getM()) | if (tile>= board.getN()*board.getM()) | ||||
// The first one here is the winner | // The first one here is the winner | ||||
@@ -275,7 +290,7 @@ public class Game { | |||||
int numOfLadders = 3; | int numOfLadders = 3; | ||||
int numOfApples = 6; | int numOfApples = 6; | ||||
int numOfPlayers = 2; | int numOfPlayers = 2; | ||||
boolean verbose = false; | |||||
boolean verbose = true; | |||||
// Print caption | // Print caption | ||||
System.out.println("================== Snake Game =================="); | System.out.println("================== Snake Game =================="); | ||||
@@ -290,14 +305,14 @@ public class Game { | |||||
// Player registration, the one is cheater | // Player registration, the one is cheater | ||||
for (int i=0 ; i<numOfPlayers && i<MAX_PLAYERS; ++i) { | for (int i=0 ; i<numOfPlayers && i<MAX_PLAYERS; ++i) { | ||||
if (i == 0) | if (i == 0) | ||||
game.registerHeuristicPlayer(i+1, String.format("Player %d", i+1)); | |||||
game.registerMinMaxPlayer(i+1, String.format("Player %d", i+1)); | |||||
else | else | ||||
game.registerPlayer(i+1, String.format("Player %d", i+1)); | game.registerPlayer(i+1, String.format("Player %d", i+1)); | ||||
} | } | ||||
game.setTurns(game.getPlayers()); // Choose play order | |||||
game.setTurns(game.getPlayers()); // Choose play order | |||||
game.board.setPlayers(game.getPlayers()); // inform the board with the players | |||||
Player winner; | Player winner; | ||||
do // Keep going until someone finishes | |||||
do // Keep going until someone finishes | |||||
winner = game.round (verbose); | winner = game.round (verbose); | ||||
while (winner == null | while (winner == null | ||||
&& game.getRound() < MAX_GAME_ROUNDS); | && game.getRound() < MAX_GAME_ROUNDS); | ||||
@@ -31,8 +31,8 @@ public class HeuristicPlayer | |||||
* @param name The name of the player | * @param name The name of the player | ||||
* @param board Reference to the board the player will play on. | * @param board Reference to the board the player will play on. | ||||
*/ | */ | ||||
HeuristicPlayer (int playerId, String name, Board board) { | |||||
super (playerId, name, board); | |||||
HeuristicPlayer (int playerId, String name) { | |||||
super (playerId, name); | |||||
path = new ArrayList<Integer[]>(); | path = new ArrayList<Integer[]>(); | ||||
} | } | ||||
/* @} */ | /* @} */ | ||||
@@ -0,0 +1,290 @@ | |||||
package net.hoo2.auth.dsproject.snake; | |||||
import java.util.*; | |||||
/** | |||||
* @class MinMaxPlayer | |||||
* @brief Represent a MinMax Player in the Game | |||||
* | |||||
* The players are playing in a round-robin sequence and we keep track | |||||
* for each one of them their playing order, score and place on the board. | |||||
* This kind of player, is a cheater. He can control the dice. Not fair dude. | |||||
* | |||||
* @author Christos Choutouridis AEM:8997 | |||||
* @email cchoutou@ece.auth.gr | |||||
*/ | |||||
public class MinMaxPlayer | |||||
extends Player { | |||||
static final int MINIMAX_TREE_DEPTH = 2; /**< The maximum depth of the minimax tree */ | |||||
/** @name Constructors */ | |||||
/** @{ */ | |||||
/** Default doing nothing constructor */ | |||||
public MinMaxPlayer() { | |||||
super (); | |||||
path = new ArrayList<Integer[]>(); | |||||
} | |||||
/** | |||||
* @brief The main constructor | |||||
* | |||||
* This creates a player for the game | |||||
* @param playerId The player's to create | |||||
* @param name The name of the player | |||||
* @param board Reference to the board the player will play on. | |||||
*/ | |||||
MinMaxPlayer (int playerId, String name) { | |||||
super (playerId, name); | |||||
path = new ArrayList<Integer[]>(); | |||||
} | |||||
/* @} */ | |||||
/** @name Get/Set interface */ | |||||
/** @{ */ | |||||
ArrayList<Integer[]> getPath() { return path; } | |||||
void setPath (ArrayList<Integer[]> path) { | |||||
this.path = path; | |||||
} | |||||
/** @} */ | |||||
/** | |||||
* Override dice functionality for the player | |||||
* @return As this is called from the game only to select playing order | |||||
* we cheat and return 1 | |||||
*/ | |||||
@Override | |||||
int dice () { | |||||
return 1; | |||||
} | |||||
/** | |||||
* Override get the next move after the user's move | |||||
* @param board The board in which we play on | |||||
* @param tile The initial tile | |||||
* @return The the move as an array | |||||
* See @ref Node.nodeMove | |||||
*/ | |||||
@Override | |||||
int[] getNextMove (Board board, int tile) { | |||||
int [] ret = new int[4]; | |||||
Node root = new Node (board); | |||||
createMySubtree(root, 1, tile, selectOpponent(board).getTile()); | |||||
// Evaluate each possible dice result and find the better one | |||||
Node r = MaxValue (root, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); | |||||
// Do the move and get the move data | |||||
Integer[] move_data = Arrays.stream (move (board, tile, r.getNodeMove()[3], true)) | |||||
.boxed() | |||||
.toArray(Integer[]::new); | |||||
// Store the move data | |||||
path.add(move_data); | |||||
ret[0] = move_data[MOVE_TILE_IDX]; | |||||
ret[1] = move_data[MOVE_INITTILE_IDX]; | |||||
ret[2] = move_data[MOVE_POINTS_IDX]; | |||||
ret[3] = move_data[MOVE_ROLL_IDX]; | |||||
return ret; // return the new tile position | |||||
} | |||||
/** | |||||
* The MinMax statistics version | |||||
* @param verbose Flag to select the verbosity | |||||
* @param sum Flag to select if we need to print a summarize of the user history | |||||
*/ | |||||
@Override | |||||
void statistics (boolean verbose, boolean sum) { | |||||
if (sum) { | |||||
// If we run the summarize | |||||
int nSnakes =0; | |||||
int nLadders =0; | |||||
int nRedApples =0; | |||||
int nBlackApples =0; | |||||
// Calculate frequencies | |||||
for (int i=0 ; i<path.size() ; ++i) { | |||||
nSnakes += path.get(i)[MOVE_SNAKES_IDX]; | |||||
nLadders+= path.get(i)[MOVE_LADDERS_IDX]; | |||||
nRedApples += path.get(i)[MOVE_RED_APPLES_IDX]; | |||||
nBlackApples += path.get(i)[MOVE_BLACK_APPLES_IDX]; | |||||
} | |||||
// Print the results | |||||
System.out.println(""); | |||||
System.out.println("*** Statistics for " + name + " ***"); | |||||
System.out.println(" Number of Snake bites : " + nSnakes); | |||||
System.out.println(" Number of Ladders used : " + nLadders); | |||||
System.out.println(" Number of Red Apples eaten : " + nRedApples); | |||||
System.out.println(" Number of Black Apples eaten: " + nBlackApples); | |||||
} | |||||
else | |||||
// Call the base version | |||||
super.statistics(verbose, sum); | |||||
} | |||||
/** @name Private helper API */ | |||||
/** @{ */ | |||||
/** | |||||
* Select the the best Opponent | |||||
* @param board Reference to current board | |||||
* @return Reference to the player who is most far in the board | |||||
*/ | |||||
private Player selectOpponent (Board board) { | |||||
Player opp = null; | |||||
int max_tile = Integer.MIN_VALUE; | |||||
for (Player p : board.getPlayers()) { | |||||
if (p == this) // Please do not return me | |||||
continue; | |||||
if (opp == null) opp = p; // init variable | |||||
if (p.getTile() > max_tile) { // max filtering | |||||
opp = p; | |||||
max_tile = p.getTile(); | |||||
} | |||||
} | |||||
return opp; | |||||
} | |||||
/** | |||||
* 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 tile the tile of MinMax player | |||||
* @param oppTile the tile of the best opponent | |||||
*/ | |||||
private void createMySubtree (Node parent, int depth, int tile, int oppTile) { | |||||
int [] moveData; | |||||
int [] nodeMove; | |||||
for (int roll = 1 ; roll <= 6 ; ++roll) { | |||||
Board nodeBoard = new Board (parent.getNodeBoard()); // clone board | |||||
moveData = move (nodeBoard, tile, roll, false); // simulate move | |||||
nodeMove = new int[4]; // create nodeMove | |||||
nodeMove[0] = moveData[MOVE_TILE_IDX]; | |||||
nodeMove[1] = moveData[MOVE_INITTILE_IDX]; | |||||
nodeMove[2] = moveData[MOVE_POINTS_IDX]; | |||||
nodeMove[3] = moveData[MOVE_ROLL_IDX]; | |||||
// make child Node | |||||
Node child = new Node (parent, depth, nodeMove, nodeBoard); | |||||
parent.addChild(child); // add child to tree | |||||
createOppSubtree (child, depth+1, nodeMove[0], oppTile); | |||||
} | |||||
} | |||||
/** | |||||
* 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 tile the tile of MinMax player | |||||
* @param oppTile the tile of the best opponent | |||||
*/ | |||||
private void createOppSubtree (Node parent, int depth, int tile, int oppTile) { | |||||
int [] moveData; | |||||
int [] nodeMove; | |||||
for (int roll =1 ; roll <= 6 ; ++roll) { | |||||
Board nodeBoard = new Board(parent.getNodeBoard()); // clone board | |||||
moveData = move (nodeBoard, oppTile, roll, false); // simulate move | |||||
nodeMove = new int[4]; | |||||
nodeMove[0] = moveData[MOVE_TILE_IDX]; | |||||
nodeMove[1] = moveData[MOVE_INITTILE_IDX]; | |||||
nodeMove[2] = moveData[MOVE_POINTS_IDX]; | |||||
nodeMove[3] = moveData[MOVE_ROLL_IDX]; | |||||
Node child = new Node (parent, depth, nodeMove, nodeBoard); // make child Node | |||||
parent.addChild(child); // add child to tree | |||||
if (depth >= MINIMAX_TREE_DEPTH) { | |||||
child.setNodeEvaluation( | |||||
evaluate(parent.getNodeMove()[0], // our tile | |||||
parent.getNodeMove()[0] - parent.getNodeMove()[1], // our steps | |||||
parent.getNodeMove()[2], // our points | |||||
nodeMove[0]) // opponent tile | |||||
); | |||||
} | |||||
else { | |||||
createMySubtree (child, depth+1, tile, nodeMove[0]); | |||||
} | |||||
} | |||||
} | |||||
/** | |||||
* The main evaluation function | |||||
* @param tile The current tile of the player | |||||
* @param steps The total steps of the move | |||||
* @param points The total points of the move | |||||
* @param oppTile The tile of the best opponent | |||||
* @return The evaluation of the roll | |||||
*/ | |||||
private double evaluate (int tile, int steps, int points, int oppTile) { | |||||
if (tile > oppTile) | |||||
return 0.65*steps + 0.35*points; | |||||
else | |||||
return 0.8*steps + 0.2*points - (oppTile - tile)*0.5; | |||||
} | |||||
/** | |||||
* The Minimax recursive function for the maximizing part | |||||
* @param s The current Node | |||||
* @param a The alpha for pruning | |||||
* @param b The beta for pruning | |||||
* @return The selected Node | |||||
*/ | |||||
private Node MaxValue (Node s, double a, double b) { | |||||
if (s.getChildren() == null) | |||||
return s; | |||||
else { | |||||
Node r = null; | |||||
double vv, v = Double.NEGATIVE_INFINITY; | |||||
for (Node c : s.getChildren()) { | |||||
if (r == null) r = c; | |||||
vv = MinValue (c, a, b).getNodeEvaluation(); | |||||
//v = max (v, vv); | |||||
if (vv > v) { | |||||
v = vv; | |||||
r = c; | |||||
} | |||||
if (vv >= b) | |||||
return c; // prune | |||||
a = max (a, vv); | |||||
} | |||||
return r; | |||||
} | |||||
} | |||||
/** | |||||
* The Minimax recursive function for the minimizing part | |||||
* @param s The current Node | |||||
* @param a The alpha for pruning | |||||
* @param b The beta for pruning | |||||
* @return The selected Node | |||||
*/ | |||||
private Node MinValue (Node s, double a, double b) { | |||||
if (s.getChildren() == null) | |||||
return s; | |||||
else { | |||||
Node r = null; | |||||
double vv, v = Double.POSITIVE_INFINITY; | |||||
for (Node c : s.getChildren()) { | |||||
if (r == null) r = c; | |||||
vv = MaxValue (c, a, b).getNodeEvaluation(); | |||||
//v = min (v, vv); | |||||
if (vv < v) { | |||||
v = vv; | |||||
r = c; | |||||
} | |||||
if (vv <= a) | |||||
return c; // prune | |||||
b = min (b, vv); | |||||
} | |||||
return r; | |||||
} | |||||
} | |||||
/** @return the minimum of x and y */ | |||||
private static double min (double x, double y) { | |||||
return (x < y) ? x : y; | |||||
} | |||||
/** return the maximum of x and y */ | |||||
private static double max (double x, double y) { | |||||
return (x > y) ? x : y; | |||||
} | |||||
/** @} */ | |||||
/** @name Data members package access only */ | |||||
/** @{ */ | |||||
private ArrayList<Integer[]> path; /**< Players history as required */ | |||||
/** @} */ | |||||
} |
@@ -0,0 +1,102 @@ | |||||
package net.hoo2.auth.dsproject.snake; | |||||
import java.util.*; | |||||
/** | |||||
* @class Node | |||||
* @brief Represent a node in the Minimax tre | |||||
* | |||||
* @author Christos Choutouridis AEM:8997 | |||||
* @email cchoutou@ece.auth.gr | |||||
*/ | |||||
class Node { | |||||
/** @name Constructors */ | |||||
/** @{ */ | |||||
/** Null initialize constructor */ | |||||
Node () { } | |||||
/** The main constructor for the Node */ | |||||
Node (Node parent, int nodeDepth, int [] nodeMove, Board nodeBoard) { | |||||
this.parent = parent; | |||||
this.children = null; | |||||
this.nodeDepth = nodeDepth; | |||||
this.nodeMove = nodeMove; | |||||
this.nodeBoard = nodeBoard; | |||||
this.nodeEvaluation = 0; | |||||
} | |||||
/** 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; | |||||
} | |||||
/**@} */ | |||||
/** @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; } | |||||
/** 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; | |||||
} | |||||
/**@}*/ | |||||
/** @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 */ | |||||
/**@}*/ | |||||
} |
@@ -32,7 +32,6 @@ public class Player { | |||||
Player () { | Player () { | ||||
playerId = score = tile = 0; | playerId = score = tile = 0; | ||||
name = ""; | name = ""; | ||||
board = null; | |||||
lastMove = new int[MOVE_DATA_SIZE]; | lastMove = new int[MOVE_DATA_SIZE]; | ||||
dryMove = new int[MOVE_DATA_SIZE]; | dryMove = new int[MOVE_DATA_SIZE]; | ||||
} | } | ||||
@@ -44,10 +43,9 @@ public class Player { | |||||
* @param name The name of the player | * @param name The name of the player | ||||
* @param board Reference to the board the player will play on. | * @param board Reference to the board the player will play on. | ||||
*/ | */ | ||||
Player (int playerId, String name, Board board) { | |||||
Player (int playerId, String name) { | |||||
this.playerId = playerId; | this.playerId = playerId; | ||||
this.name = name; | this.name = name; | ||||
this.board = board; | |||||
score = 0; | score = 0; | ||||
tile = 0; | tile = 0; | ||||
lastMove = new int[MOVE_DATA_SIZE]; | lastMove = new int[MOVE_DATA_SIZE]; | ||||
@@ -69,12 +67,7 @@ public class Player { | |||||
void setScore (int score) { | void setScore (int score) { | ||||
this.score = score; | this.score = score; | ||||
} | } | ||||
/** Get reference to Board */ | |||||
Board getBoard () { return board; } | |||||
/** Set Board reference */ | |||||
void setBoard (Board board) { | |||||
this.board = board; | |||||
} | |||||
/** Get tile */ | /** Get tile */ | ||||
int getTile () { return tile; } | int getTile () { return tile; } | ||||
/** Set tile */ | /** Set tile */ | ||||
@@ -109,16 +102,25 @@ public class Player { | |||||
/** | /** | ||||
* Get the next tile after the user's move | * Get the next tile after the user's move | ||||
* @param board The board in which we play on | |||||
* @param tile The initial tile | * @param tile The initial tile | ||||
* @return The tile after the move | |||||
* @return The the move as an array | |||||
* [0]: Tile after move | |||||
* [1]: The roll of the dice | |||||
* @note | * @note | ||||
* We add this move() wrapper in order to provide polymorphism to | * We add this move() wrapper in order to provide polymorphism to | ||||
* Player class hierarchy and to be sure that we are not braking the | * Player class hierarchy and to be sure that we are not braking the | ||||
* Liskov substitution principle | * Liskov substitution principle | ||||
* @see https://en.wikipedia.org/wiki/Liskov_substitution_principle | * @see https://en.wikipedia.org/wiki/Liskov_substitution_principle | ||||
*/ | */ | ||||
int getNextMove (int tile) { | |||||
return move (tile, dice(), true)[MOVE_TILE_IDX]; | |||||
int[] getNextMove (Board board, int tile) { | |||||
int [] ret = new int[4]; // allocate move memory | |||||
move (board, tile, dice(), true); // make the move | |||||
ret[0] = dryMove[MOVE_TILE_IDX]; | |||||
ret[1] = dryMove[MOVE_INITTILE_IDX]; | |||||
ret[2] = dryMove[MOVE_POINTS_IDX]; | |||||
ret[3] = dryMove[MOVE_ROLL_IDX]; | |||||
return ret; | |||||
} | } | ||||
/** | /** | ||||
@@ -149,7 +151,7 @@ public class Player { | |||||
* We could also had members like: <pre> MoveData previous; </pre> to help us further. | * We could also had members like: <pre> MoveData previous; </pre> to help us further. | ||||
* We kept this representation just because it was a requirement. | * We kept this representation just because it was a requirement. | ||||
*/ | */ | ||||
int [] move (int tile, int roll, boolean run) { | |||||
int [] move (Board board, int tile, int roll, boolean run) { | |||||
int t; | int t; | ||||
Arrays.fill(dryMove, 0); | Arrays.fill(dryMove, 0); | ||||
@@ -161,7 +163,8 @@ public class Player { | |||||
do { | do { | ||||
keepGoing = false; | keepGoing = false; | ||||
// Check apples | // Check apples | ||||
if ((t = board.checkApple(tile, run)) != 0) { | |||||
//if ((t = board.checkApple(tile, run)) != 0) { | |||||
if ((t = board.checkApple(tile, true)) != 0) { | |||||
dryMove[MOVE_POINTS_IDX] += t; | dryMove[MOVE_POINTS_IDX] += t; | ||||
if (t > 0) | if (t > 0) | ||||
++dryMove[MOVE_RED_APPLES_IDX]; | ++dryMove[MOVE_RED_APPLES_IDX]; | ||||
@@ -169,7 +172,8 @@ public class Player { | |||||
++dryMove[MOVE_BLACK_APPLES_IDX]; | ++dryMove[MOVE_BLACK_APPLES_IDX]; | ||||
} | } | ||||
// Check ladder | // Check ladder | ||||
if ((t = board.checkLadder(tile, run)) != tile) { | |||||
//if ((t = board.checkLadder(tile, run)) != tile) { | |||||
if ((t = board.checkLadder(tile, true)) != tile) { | |||||
tile = t; | tile = t; | ||||
++dryMove[MOVE_LADDERS_IDX]; | ++dryMove[MOVE_LADDERS_IDX]; | ||||
keepGoing = true; | keepGoing = true; | ||||
@@ -226,7 +230,6 @@ public class Player { | |||||
int playerId; /**< Player's ID */ | int playerId; /**< Player's ID */ | ||||
String name; /**< Player's name */ | String name; /**< Player's name */ | ||||
int score; /**< Player's score */ | int score; /**< Player's score */ | ||||
Board board; /**< Reference to current board */ | |||||
int tile; /**< Player's tile location */ | int tile; /**< Player's tile location */ | ||||
int[] lastMove; /**< move() return data for statistics. These are only valid after a true move */ | int[] lastMove; /**< move() return data for statistics. These are only valid after a true move */ | ||||
private int [] dryMove; /**< Fake (dry run) move return buffer */ | private int [] dryMove; /**< Fake (dry run) move return buffer */ | ||||