Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
b58da8a26e | ||
|
7f5f3f34e8 | ||
|
3b3eda4090 | ||
|
b583d63095 | ||
|
da8ad7259a | ||
|
ee3dfb7cea | ||
|
984f537cb8 | ||
|
4b3a936391 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,8 +1,8 @@
|
|||||||
*bin/*
|
*bin/*
|
||||||
*doc/*
|
*doc/*
|
||||||
*report/*
|
|
||||||
*deliverable/*
|
*deliverable/*
|
||||||
*.project
|
*.project
|
||||||
*.classpath
|
*.classpath
|
||||||
*.pdf
|
*.pdf
|
||||||
|
|
||||||
|
/bin/
|
||||||
|
BIN
report/Snake.odt
Normal file
BIN
report/Snake.odt
Normal file
Binary file not shown.
BIN
report/resources/poster.jpg
Normal file
BIN
report/resources/poster.jpg
Normal file
Binary file not shown.
After Width: | Height: | Size: 96 KiB |
@ -1,7 +1,5 @@
|
|||||||
package net.hoo2.auth.dsproject.snake;
|
package net.hoo2.auth.dsproject.snake;
|
||||||
|
|
||||||
import java.lang.Math;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Apple
|
* @class Apple
|
||||||
* @brief Represent an Apple in the Board.
|
* @brief Represent an Apple in the Board.
|
||||||
|
@ -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
|
||||||
@ -223,11 +234,11 @@ public class Board {
|
|||||||
* @param tile The tile to check
|
* @param tile The tile to check
|
||||||
* @return The result tile
|
* @return The result tile
|
||||||
*/
|
*/
|
||||||
int checkLadder (int tile) {
|
int checkLadder (int tile, boolean climb) {
|
||||||
for (int i =0 ; i<ladders.length ; ++i) {
|
for (int i =0 ; i<ladders.length ; ++i) {
|
||||||
if (ladders[i].getDownStepId() == tile &&
|
if (ladders[i].getDownStepId() == tile &&
|
||||||
ladders[i].getBroken() == false) {
|
ladders[i].getBroken() == false) {
|
||||||
ladders[i].setBroken(true);
|
ladders[i].setBroken(climb);
|
||||||
return ladders[i].getUpStepId();
|
return ladders[i].getUpStepId();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,13 +250,14 @@ public class Board {
|
|||||||
* @param tile The tile to check
|
* @param tile The tile to check
|
||||||
* @return The score difference
|
* @return The score difference
|
||||||
*/
|
*/
|
||||||
int checkApple (int tile) {
|
int checkApple (int tile, boolean eat) {
|
||||||
int ds =0; // delta-score
|
int ds =0; // delta-score
|
||||||
for (int i =0 ; i<apples.length ; ++i) {
|
for (int i =0 ; i<apples.length ; ++i) {
|
||||||
if (apples[i].getAppleTileId() == tile) {
|
if (apples[i].getAppleTileId() == tile) {
|
||||||
// eat it
|
|
||||||
ds = apples[i].getPoints();
|
ds = apples[i].getPoints();
|
||||||
apples[i].setPoints(0);
|
// eat it
|
||||||
|
if (eat)
|
||||||
|
apples[i].setPoints(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ds;
|
return ds;
|
||||||
@ -551,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; /**< board's copy of players reference vector */
|
||||||
/** @} */
|
/** @} */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@ package net.hoo2.auth.dsproject.snake;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @mainpage
|
* @mainpage
|
||||||
* @title Snake game project. -- Part 1 --
|
* @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:
|
||||||
@ -13,9 +13,7 @@ package net.hoo2.auth.dsproject.snake;
|
|||||||
* @author Christos Choutouridis AEM:8997
|
* @author Christos Choutouridis AEM:8997
|
||||||
* @email cchoutou@ece.auth.gr
|
* @email cchoutou@ece.auth.gr
|
||||||
*/
|
*/
|
||||||
|
import java.util.*;
|
||||||
import java.lang.Math;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Game
|
* @class Game
|
||||||
@ -31,40 +29,74 @@ import java.util.ArrayList;
|
|||||||
public class Game {
|
public class Game {
|
||||||
/** @name Constants */
|
/** @name Constants */
|
||||||
/**@{ */
|
/**@{ */
|
||||||
static final int MAX_PLAYERS = 4; /**< The maximum number of allowed players in the game */
|
static final int MAX_PLAYERS = 6; /**< The maximum number of allowed players in the game */
|
||||||
static final int MAX_GAME_ROUNDS = 10000; /**< the maximum allowed round of the game */
|
static final int MAX_GAME_ROUNDS = 1000; /**< the maximum allowed round of the game */
|
||||||
/**@} */
|
/**@} */
|
||||||
|
|
||||||
/** @name Private data members */
|
/** @name Private data members */
|
||||||
/** @{ */
|
/** @{ */
|
||||||
private int round; /**< The current round of the game */
|
private int round; /**< The current round of the game */
|
||||||
private Board board; /**< A reference to board */
|
private Board board; /**< A reference to board */
|
||||||
private ArrayList<Player> players; /**< A reference to players */
|
private ArrayList<Player> players; /**< A reference to players */
|
||||||
|
private Map<Integer, Integer> playingOrder; /**< A Map with {PlayerID, Dice-roll} pairs
|
||||||
|
@note
|
||||||
|
This way of storing the playing order is unnecessary.
|
||||||
|
The old style was far more better. We had a turn variable
|
||||||
|
in each Player and we used to sort the players vector based on
|
||||||
|
that value. This made the rest of the program simpler, faster, easer.
|
||||||
|
We have adopt the above approach just because it is one of the
|
||||||
|
homework requirements.
|
||||||
|
*/
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** @name private api */
|
/** @name private api */
|
||||||
/** @{ */
|
/** @{ */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dice functionality
|
* Search the players already in the pairs vector and compare their turn to play
|
||||||
* @return An integer in the range [1 .. 6]
|
|
||||||
*/
|
|
||||||
private int _dice () {
|
|
||||||
return (int)(1 + Math.random()*5);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Search the players already in the players vector and compare their turn to play
|
|
||||||
* with the result of a dice. If there is another one with the same dice result return true.
|
* with the result of a dice. If there is another one with the same dice result return true.
|
||||||
*
|
*
|
||||||
* @param turn The dice result to check in order to find player's turn to play
|
* @param roll The dice result to check in order to find player's turn to play
|
||||||
* @param players Reference to already register players
|
* @param pairs Reference to already register players and their dice result
|
||||||
* @return True if there is another player with the same dice result
|
* @return True if there is another player with the same dice result
|
||||||
*/
|
*/
|
||||||
private boolean _search (int die, ArrayList<Player> players) {
|
private boolean _search (int roll, ArrayList<Integer[]> pairs) {
|
||||||
for (int i =0; i<players.size() ; ++i)
|
for (int i =0; i<pairs.size() ; ++i)
|
||||||
if (players.get(i).getTurn() == die)
|
if (pairs.get(i)[1] == roll)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort the pairs vector using second parameter witch represent the
|
||||||
|
* dice roll. We use bubblesort for the implementation.
|
||||||
|
* @param pairs A vector with {PlayerId, dice-roll} pairs to sort
|
||||||
|
*/
|
||||||
|
private void _sort (ArrayList<Integer[]> pairs) {
|
||||||
|
Integer[] temp;
|
||||||
|
for (int i=pairs.size()-1 ; i>0 ; --i) {
|
||||||
|
for (int j =0 ; j<i ; ++j) {
|
||||||
|
if (pairs.get(j)[1] > pairs.get(i)[1]) {
|
||||||
|
temp = pairs.get(i);
|
||||||
|
pairs.set(i, pairs.get(j));
|
||||||
|
pairs.set(j, temp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function to get a player from players vector using it's ID
|
||||||
|
* @param playerId The player's ID to seek
|
||||||
|
* @return Reference to Player or null
|
||||||
|
*/
|
||||||
|
private Player _getPlayer(int playerId) {
|
||||||
|
for (Player p : players) {
|
||||||
|
if (p.getPlayerId() == playerId)
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
|
|
||||||
@ -75,6 +107,8 @@ public class Game {
|
|||||||
round = 0;
|
round = 0;
|
||||||
board = new Board();
|
board = new Board();
|
||||||
players = new ArrayList<>();
|
players = new ArrayList<>();
|
||||||
|
playingOrder
|
||||||
|
= new HashMap<Integer, Integer>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -92,6 +126,8 @@ public class Game {
|
|||||||
// delegate constructors
|
// delegate constructors
|
||||||
board = new Board (N, M, numOfSnakes, numOfLadders, numOfApples);
|
board = new Board (N, M, numOfSnakes, numOfLadders, numOfApples);
|
||||||
players = new ArrayList<>();
|
players = new ArrayList<>();
|
||||||
|
playingOrder
|
||||||
|
= new HashMap<Integer, Integer>();
|
||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
@ -118,6 +154,12 @@ public class Game {
|
|||||||
void setPlayers(ArrayList<Player> players) {
|
void setPlayers(ArrayList<Player> players) {
|
||||||
this.players = players;
|
this.players = players;
|
||||||
}
|
}
|
||||||
|
/** Get reference to playingOrder Map */
|
||||||
|
Map<Integer, Integer> getPlayingOrder () { return playingOrder; }
|
||||||
|
/** Set the playingOrder Map */
|
||||||
|
void setPlayingOrder (Map<Integer, Integer> playingOrder) {
|
||||||
|
this.playingOrder = playingOrder;
|
||||||
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** @name Public functionality */
|
/** @name Public functionality */
|
||||||
@ -131,29 +173,71 @@ 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
|
||||||
|
* @param playerId The player ID to use
|
||||||
|
* @param name The player name to use
|
||||||
|
* @return The status of the operation
|
||||||
|
*/
|
||||||
|
boolean registerMinMaxPlayer (int playerId, String name) {
|
||||||
|
if (players.size() >= MAX_PLAYERS)
|
||||||
|
return false;
|
||||||
|
//players.add(new HeuristicPlayer(playerId, name, board));
|
||||||
|
players.add(new MinMaxPlayer(playerId, name));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the playing order of players
|
* @brief Set the playing order of players
|
||||||
* This function emulates the classic roll of dice to decide which player
|
* This function emulates the classic roll of dice to decide which player
|
||||||
* plays first which second and so on.
|
* plays first which second and so on.
|
||||||
*/
|
*/
|
||||||
void playOrder () {
|
Map<Integer, Integer> setTurns (ArrayList<Player> players) {
|
||||||
int d;
|
int[] pairData = new int[2]; // We use plain int[] to create an Integer[]
|
||||||
for (int i =0 ; i<players.size() ; ++i) {
|
Integer[] PairData; // The Integer[] to push to ArrayList<>
|
||||||
do
|
ArrayList<Integer[]>
|
||||||
// Keep rolling the dice as the die belongs to another user
|
pairList = new ArrayList<>(); // Use use ArrayList before Map in order to sort
|
||||||
d = _dice();
|
|
||||||
while (_search (d, players));
|
|
||||||
players.get(i).setTurn(d);
|
|
||||||
}
|
|
||||||
// Sort players vector
|
|
||||||
players.sort((p1, p2) ->
|
|
||||||
Integer.compare (p1.getTurn(), p2.getTurn())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
for (int d, i =0 ; i<players.size() ; ++i) {
|
||||||
|
do
|
||||||
|
// Keep rolling the dice until we get a unique value
|
||||||
|
d = players.get(i).dice ();
|
||||||
|
while (_search (d, pairList));
|
||||||
|
// Make Integer[] pair
|
||||||
|
pairData[0] = players.get(i).getPlayerId();
|
||||||
|
pairData[1] = d;
|
||||||
|
PairData = Arrays.stream(pairData)
|
||||||
|
.boxed()
|
||||||
|
.toArray(Integer[]::new);
|
||||||
|
pairList.add(PairData); // Add to vector
|
||||||
|
}
|
||||||
|
// Sort playeingOrger
|
||||||
|
_sort (pairList);
|
||||||
|
// Make and return the Map
|
||||||
|
for (int i =0 ; i<pairList.size() ; ++i) {
|
||||||
|
playingOrder.put(pairList.get(i)[0], pairList.get(i)[1]);
|
||||||
|
}
|
||||||
|
return playingOrder;
|
||||||
|
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Sort the players according to their score
|
* Sort the players according to their score
|
||||||
*/
|
*/
|
||||||
@ -166,19 +250,22 @@ public class Game {
|
|||||||
/**
|
/**
|
||||||
* A game round. In each round every player plays when is his turn
|
* A game round. In each round every player plays when is his turn
|
||||||
*
|
*
|
||||||
|
* @param verbose Flag to indicate how much we print to console
|
||||||
* @return The winner if we have one, or null
|
* @return The winner if we have one, or null
|
||||||
*/
|
*/
|
||||||
Player round () {
|
Player round (boolean verbose) {
|
||||||
int [] mret;
|
int tile;
|
||||||
++round; // keep track of round
|
++round; // keep track of round
|
||||||
|
|
||||||
// traverse the players vector and move each player on the board
|
// traverse the players vector and move each player on the board
|
||||||
// using a dice throw
|
// using a dice throw
|
||||||
for (int i =0 ; i<players.size() ; ++i) {
|
for (Integer pid : playingOrder.keySet()) {
|
||||||
mret = players.get(i).move (players.get(i).getTile(), _dice());
|
Player p = _getPlayer(pid);
|
||||||
if (mret[0]>= board.getN()*board.getM())
|
tile = p.getNextMove (board, p.getTile())[0];
|
||||||
|
p.statistics(verbose, false);
|
||||||
|
if (tile>= board.getN()*board.getM())
|
||||||
// The first one here is the winner
|
// The first one here is the winner
|
||||||
return players.get(i);
|
return p;
|
||||||
}
|
}
|
||||||
return null; // No one finished yet
|
return null; // No one finished yet
|
||||||
}
|
}
|
||||||
@ -203,6 +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 = true;
|
||||||
|
|
||||||
// Print caption
|
// Print caption
|
||||||
System.out.println("================== Snake Game ==================");
|
System.out.println("================== Snake Game ==================");
|
||||||
@ -214,14 +302,18 @@ public class Game {
|
|||||||
Game game = new Game (lines, columns, numOfSnakes, numOfLadders, numOfApples);
|
Game game = new Game (lines, columns, numOfSnakes, numOfLadders, numOfApples);
|
||||||
// game.getBoard().createElementBoard(); // Not explicitly required
|
// game.getBoard().createElementBoard(); // Not explicitly required
|
||||||
|
|
||||||
// Player registration
|
// 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) {
|
||||||
game.registerPlayer(i+1, String.format("Player %d", i+1));
|
if (i == 0)
|
||||||
game.playOrder(); // Choose play order
|
game.registerMinMaxPlayer(i+1, String.format("Player %d", i+1));
|
||||||
|
else
|
||||||
|
game.registerPlayer(i+1, String.format("Player %d", i+1));
|
||||||
|
}
|
||||||
|
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 ();
|
winner = game.round (verbose);
|
||||||
while (winner == null
|
while (winner == null
|
||||||
&& game.getRound() < MAX_GAME_ROUNDS);
|
&& game.getRound() < MAX_GAME_ROUNDS);
|
||||||
if (game.getRound() == MAX_GAME_ROUNDS) {
|
if (game.getRound() == MAX_GAME_ROUNDS) {
|
||||||
@ -231,8 +323,10 @@ public class Game {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print the results
|
// Print the results
|
||||||
System.out.println("***** Game finished *****");
|
System.out.println("*** Game finished ***");
|
||||||
System.out.println("");
|
System.out.println("");
|
||||||
|
System.out.println("");
|
||||||
|
System.out.println("*** Game Results ***");
|
||||||
System.out.println("Rounds: " + game.getRound());
|
System.out.println("Rounds: " + game.getRound());
|
||||||
System.out.println("Winner: " + winner.getName() + " [" + winner.getScore() +" points]");
|
System.out.println("Winner: " + winner.getName() + " [" + winner.getScore() +" points]");
|
||||||
System.out.println("Score: ");
|
System.out.println("Score: ");
|
||||||
@ -245,5 +339,13 @@ public class Game {
|
|||||||
else
|
else
|
||||||
System.out.println(" " +p.getName() + ": " + p.getScore() +" points");
|
System.out.println(" " +p.getName() + ": " + p.getScore() +" points");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Print the extra statistics for the heuristic player only
|
||||||
|
// We use a little reflection for that
|
||||||
|
for (Player p : game.getPlayers()) {
|
||||||
|
if (p.getClass().getSimpleName().equals("HeuristicPlayer"))
|
||||||
|
p.statistics(verbose, true);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
140
src/net/hoo2/auth/dsproject/snake/HeuristicPlayer.java
Normal file
140
src/net/hoo2/auth/dsproject/snake/HeuristicPlayer.java
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
package net.hoo2.auth.dsproject.snake;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class HeuristicPlayer
|
||||||
|
* @brief Represent a Heuristic 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 HeuristicPlayer
|
||||||
|
extends Player {
|
||||||
|
|
||||||
|
/** @name Constructors */
|
||||||
|
/** @{ */
|
||||||
|
/** Default doing nothing constructor */
|
||||||
|
public HeuristicPlayer() {
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
HeuristicPlayer (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 tile after the user's move
|
||||||
|
* @param tile The initial tile
|
||||||
|
* @return The tile after the move
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
int getNextMove (int tile) {
|
||||||
|
Map<Integer, Double> moves = new HashMap<Integer, Double>();
|
||||||
|
double max = Double.NEGATIVE_INFINITY;
|
||||||
|
double ev = Double.NEGATIVE_INFINITY;
|
||||||
|
int roll = 0;
|
||||||
|
|
||||||
|
// Evaluate each possible dice result and find the better one
|
||||||
|
for (int r=1 ; r<=6 ; ++r) {
|
||||||
|
moves.put (new Integer(r), evaluate (tile, r));
|
||||||
|
if ((ev = moves.get(r)) > max) {
|
||||||
|
max = ev;
|
||||||
|
roll = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Do the move and get the move data
|
||||||
|
Integer[] move_data = Arrays.stream(move (tile, roll, true))
|
||||||
|
.boxed()
|
||||||
|
.toArray(Integer[]::new);
|
||||||
|
// Store the move data
|
||||||
|
path.add(move_data);
|
||||||
|
return tile + roll; // return the new tile position
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Heuristic statistics version
|
||||||
|
* @param verbose Flag to select the verbosity
|
||||||
|
* @param sum Flag to select if we need to print a summarize of the user hystory
|
||||||
|
*/
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main evaluation function
|
||||||
|
* @param tile The current tile of the player
|
||||||
|
* @param roll the roll to check
|
||||||
|
* @return The evaluation of the roll
|
||||||
|
*/
|
||||||
|
private double evaluate (int tile, int roll) {
|
||||||
|
int[] check = new int[MOVE_DATA_SIZE];
|
||||||
|
check = move(tile, roll, false);
|
||||||
|
|
||||||
|
return 0.65*check[MOVE_STEPS_IDX] + 0.35*check[MOVE_POINTS_IDX];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @name Data members package access only */
|
||||||
|
/** @{ */
|
||||||
|
private ArrayList<Integer[]> path; /**< Players history as required */
|
||||||
|
/** @} */
|
||||||
|
}
|
290
src/net/hoo2/auth/dsproject/snake/MinMaxPlayer.java
Normal file
290
src/net/hoo2/auth/dsproject/snake/MinMaxPlayer.java
Normal file
@ -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 */
|
||||||
|
/** @} */
|
||||||
|
}
|
102
src/net/hoo2/auth/dsproject/snake/Node.java
Normal file
102
src/net/hoo2/auth/dsproject/snake/Node.java
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package net.hoo2.auth.dsproject.snake;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class Node
|
||||||
|
* @brief Represent a node in the Minimax tree
|
||||||
|
*
|
||||||
|
* @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 */
|
||||||
|
/**@}*/
|
||||||
|
}
|
@ -1,5 +1,8 @@
|
|||||||
package net.hoo2.auth.dsproject.snake;
|
package net.hoo2.auth.dsproject.snake;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.lang.Math;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class Player
|
* @class Player
|
||||||
* @brief Represent a Player in the Game
|
* @brief Represent a Player in the Game
|
||||||
@ -11,13 +14,26 @@ package net.hoo2.auth.dsproject.snake;
|
|||||||
* @email cchoutou@ece.auth.gr
|
* @email cchoutou@ece.auth.gr
|
||||||
*/
|
*/
|
||||||
public class Player {
|
public class Player {
|
||||||
|
/** Helper variables to keep track of the move() return values @see move() */
|
||||||
|
static final int MOVE_DATA_SIZE = 9; /**< The move return data array size */
|
||||||
|
static final int MOVE_TILE_IDX = 0; /**< The index of tile */
|
||||||
|
static final int MOVE_INITTILE_IDX = 1; /**< The index of init tile */
|
||||||
|
static final int MOVE_STEPS_IDX = 2; /**< The index of steps */
|
||||||
|
static final int MOVE_ROLL_IDX = 3; /**< The index of roll */
|
||||||
|
static final int MOVE_POINTS_IDX = 4; /**< The index of points */
|
||||||
|
static final int MOVE_SNAKES_IDX = 5; /**< The index for number of snakes (Always <= 1) */
|
||||||
|
static final int MOVE_LADDERS_IDX = 6; /**< The index for number of ladders (Always <= 1) */
|
||||||
|
static final int MOVE_RED_APPLES_IDX = 7; /**< The index for number of red apples (Always <= 1) */
|
||||||
|
static final int MOVE_BLACK_APPLES_IDX = 8; /**< The index for number of black apples (Always <= 1) */
|
||||||
|
|
||||||
/** @name Constructors */
|
/** @name Constructors */
|
||||||
/** @{ */
|
/** @{ */
|
||||||
/** Default doing nothing constructor */
|
/** Default doing nothing constructor */
|
||||||
Player () {
|
Player () {
|
||||||
playerId = score = tile = turn = 0;
|
playerId = score = tile = 0;
|
||||||
name = "";
|
name = "";
|
||||||
board = null;
|
lastMove = new int[MOVE_DATA_SIZE];
|
||||||
|
dryMove = new int[MOVE_DATA_SIZE];
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* @brief The main constructor
|
* @brief The main constructor
|
||||||
@ -27,13 +43,13 @@ 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;
|
||||||
turn = 0;
|
lastMove = new int[MOVE_DATA_SIZE];
|
||||||
|
dryMove = new int[MOVE_DATA_SIZE];
|
||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
@ -51,87 +67,172 @@ 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 */
|
||||||
void setTile (int tile) {
|
void setTile (int tile) {
|
||||||
this.tile = tile;
|
this.tile = tile;
|
||||||
}
|
}
|
||||||
/** Get turn */
|
|
||||||
int getTurn () { return turn; }
|
/** Get lastMove */
|
||||||
/** Set turn */
|
int[] getLastMove () { return lastMove; }
|
||||||
void setTurn (int turn) {
|
/** Set lastMove */
|
||||||
this.turn = turn;
|
void setLastMove (int[] lastMove) {
|
||||||
|
this.lastMove = lastMove;
|
||||||
|
}
|
||||||
|
/** Get dryMove */
|
||||||
|
int[] getDryMove () { return dryMove; }
|
||||||
|
/** Set dryMove */
|
||||||
|
void setDryMove (int[] dryMove) {
|
||||||
|
this.dryMove = dryMove;
|
||||||
}
|
}
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
||||||
/** @name Exposed API members */
|
/** @name Exposed API members */
|
||||||
/** @{ */
|
/** @{ */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dice functionality for the players
|
||||||
|
* @return An integer in the range [1 .. 6]
|
||||||
|
*/
|
||||||
|
int dice () {
|
||||||
|
return (int)(1 + Math.random()*5);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the next tile 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
|
||||||
|
* [0]: Tile after move
|
||||||
|
* [1]: The roll of the dice
|
||||||
|
* @note
|
||||||
|
* We add this move() 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
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move functionality
|
* @brief Move functionality
|
||||||
* This function prints to stdout various logs about user interaction with elements
|
|
||||||
*
|
*
|
||||||
* @param tile The initial tile of the player
|
* @param tile The initial tile of the player
|
||||||
* @param die The die to play
|
* @param roll The dice roll to play
|
||||||
|
* @param run Flag to indicate if we fake the move(dry run) or we do make the move.
|
||||||
* @return
|
* @return
|
||||||
* int[0] tile after move
|
* int[MOVE_TILE_IDX (0)] tile after move
|
||||||
* int[1] number of snake bites
|
* int[MOVE_INITTILE_IDX (1)] tile before move
|
||||||
* int[2] number of ladder used
|
* int[MOVE_STEPS_IDX (2)] number of total steps for the move
|
||||||
* int[3] number of apples eaten
|
* int[MOVE_ROLL_IDX (3)] the roll of the dice
|
||||||
|
* int[MOVE_POINTS_IDX (4)] the points of the move
|
||||||
|
* int[MOVE_SNAKES_IDX (5)] number of snake bites
|
||||||
|
* int[MOVE_LADDERS_IDX (6)] number of ladders used
|
||||||
|
* int[MOVE_RED_APPLES_IDX (7)] number of red apples eaten
|
||||||
|
* int[MOVE_BLACK_APPLES_IDX (8)] number of black apples eaten
|
||||||
|
* @note
|
||||||
|
* Probably a mutable class like: <pre>
|
||||||
|
* class MoveData {
|
||||||
|
* int tile;
|
||||||
|
* int initTile;
|
||||||
|
* ...
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
* for returning value would be better, less error prone, cleaner, etc...
|
||||||
|
* We could also had members like: <pre> MoveData previous; </pre> to help us further.
|
||||||
|
* We kept this representation just because it was a requirement.
|
||||||
*/
|
*/
|
||||||
int [] move (int tile, int die) {
|
int [] move (Board board, int tile, int roll, boolean run) {
|
||||||
int [] ret = new int[4];
|
|
||||||
int t;
|
int t;
|
||||||
|
|
||||||
tile += die; // Initial move
|
Arrays.fill(dryMove, 0);
|
||||||
//System.out.println(name + " +" + die + "->tile: " + tile); //XXX: Debug only
|
dryMove[MOVE_INITTILE_IDX] = tile;
|
||||||
|
dryMove[MOVE_ROLL_IDX] = roll;
|
||||||
|
|
||||||
|
tile += roll; // Initial move
|
||||||
boolean keepGoing;
|
boolean keepGoing;
|
||||||
do {
|
do {
|
||||||
keepGoing = false;
|
keepGoing = false;
|
||||||
// Check apples
|
// Check apples
|
||||||
t = board.checkApple(tile);
|
//if ((t = board.checkApple(tile, run)) != 0) {
|
||||||
if (t != 0) {
|
if ((t = board.checkApple(tile, true)) != 0) {
|
||||||
score += t;
|
dryMove[MOVE_POINTS_IDX] += t;
|
||||||
++ret[3];
|
if (t > 0)
|
||||||
System.out.println(name + " Apple @" + tile + " " + t + " points");
|
++dryMove[MOVE_RED_APPLES_IDX];
|
||||||
|
else
|
||||||
|
++dryMove[MOVE_BLACK_APPLES_IDX];
|
||||||
}
|
}
|
||||||
// Check ladder
|
// Check ladder
|
||||||
t = board.checkLadder(tile);
|
//if ((t = board.checkLadder(tile, run)) != tile) {
|
||||||
if (t != tile) {
|
if ((t = board.checkLadder(tile, true)) != tile) {
|
||||||
System.out.println(name + " Ladder @" + tile + " new position " + t);
|
|
||||||
tile = t;
|
tile = t;
|
||||||
++ret[2];
|
++dryMove[MOVE_LADDERS_IDX];
|
||||||
keepGoing = true;
|
keepGoing = true;
|
||||||
}
|
}
|
||||||
// Check snakes
|
// Check snakes
|
||||||
t = board.checkSnake(tile);
|
if ((t = board.checkSnake(tile)) != tile) {
|
||||||
if (t != tile) {
|
|
||||||
System.out.println(name + " Ouch!! Snake @" + tile + " new position " + t);
|
|
||||||
tile = t;
|
tile = t;
|
||||||
++ret[1];
|
++dryMove[MOVE_SNAKES_IDX];
|
||||||
keepGoing = true;
|
keepGoing = true;
|
||||||
}
|
}
|
||||||
} while (keepGoing);
|
} while (keepGoing);
|
||||||
ret[0] = this.tile = tile;
|
|
||||||
return ret;
|
dryMove[MOVE_TILE_IDX] = tile;
|
||||||
|
dryMove[MOVE_STEPS_IDX]= tile - dryMove[MOVE_INITTILE_IDX];
|
||||||
|
// Check if we do run the move
|
||||||
|
if (run) {
|
||||||
|
lastMove = dryMove.clone();
|
||||||
|
this.tile = lastMove[MOVE_TILE_IDX];
|
||||||
|
score += lastMove[MOVE_POINTS_IDX];
|
||||||
|
}
|
||||||
|
return dryMove;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base statistics version
|
||||||
|
* @param verbose Flag to select the verbosity
|
||||||
|
* @param sum Flag to select if we need to print a summarize (not used here)
|
||||||
|
* @note
|
||||||
|
* We added this function because:
|
||||||
|
* 1) we need to keep the "Is a" relationship (Liskov substitution principle)
|
||||||
|
* 2) It help us get rid of the move() console output code
|
||||||
|
* 3) makes the code smaller
|
||||||
|
*/
|
||||||
|
void statistics (boolean verbose, boolean sum) {
|
||||||
|
int begin = lastMove[MOVE_INITTILE_IDX];
|
||||||
|
int roll = lastMove[MOVE_ROLL_IDX];
|
||||||
|
int last = lastMove[MOVE_TILE_IDX];
|
||||||
|
if (verbose)
|
||||||
|
System.out.println(name + " +" + roll + "->tile: " + (begin + roll));
|
||||||
|
if (lastMove[MOVE_RED_APPLES_IDX] > 0)
|
||||||
|
System.out.println(name + " Apple " + lastMove[MOVE_POINTS_IDX] + " points");
|
||||||
|
if (lastMove[MOVE_BLACK_APPLES_IDX] > 0)
|
||||||
|
System.out.println(name + " Apple " + lastMove[MOVE_POINTS_IDX] + " points");
|
||||||
|
if (lastMove[MOVE_LADDERS_IDX] > 0)
|
||||||
|
System.out.println(name + " Ladder @" + (begin + roll) + " new position " + last);
|
||||||
|
if (lastMove[MOVE_SNAKES_IDX] > 0)
|
||||||
|
System.out.println(name + " Ouch!! Snake @" + (begin + roll) + " new position " + last);
|
||||||
|
// No use of sum here
|
||||||
}
|
}
|
||||||
/**@} */
|
/**@} */
|
||||||
|
|
||||||
|
/** @name Data members package access only */
|
||||||
/** @name Data members (private) */
|
|
||||||
/** @{ */
|
/** @{ */
|
||||||
private int playerId; /**< Player's ID */
|
int playerId; /**< Player's ID */
|
||||||
private String name; /**< Player's name */
|
String name; /**< Player's name */
|
||||||
private int score; /**< Player's score */
|
int score; /**< Player's score */
|
||||||
private Board board; /**< Reference to current board */
|
int tile; /**< Player's tile location */
|
||||||
private int tile; /**< Player's tile location */
|
int[] lastMove; /**< move() return data for statistics. These are only valid after a true move */
|
||||||
private int turn; /**< Player's turn of playing */
|
private int [] dryMove; /**< Fake (dry run) move return buffer */
|
||||||
/** @} */
|
/** @} */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user