package net.hoo2.auth.dsproject.snake; import java.util.Arrays; import java.lang.Math; /** * @class Player * @brief Represent a 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. * * @author Christos Choutouridis AEM:8997 * @email cchoutou@ece.auth.gr */ 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 */ /** @{ */ /** Default doing nothing constructor */ Player () { playerId = score = tile = 0; name = ""; lastMove = new int[MOVE_DATA_SIZE]; dryMove = new int[MOVE_DATA_SIZE]; } /** * @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. */ Player (int playerId, String name) { this.playerId = playerId; this.name = name; score = 0; tile = 0; lastMove = new int[MOVE_DATA_SIZE]; dryMove = new int[MOVE_DATA_SIZE]; } /** @} */ /** @name Get/Set interface */ /** @{ */ int getPlayerId () { return playerId; } void setPlayerId (int playerId) { this.playerId = playerId; } String getName () { return name; } void setName (String name) { this.name = name; } int getScore () { return score; } void setScore (int score) { this.score = score; } /** Get tile */ int getTile () { return tile; } /** Set tile */ void setTile (int tile) { this.tile = tile; } /** Get lastMove */ int[] getLastMove () { return lastMove; } /** Set lastMove */ 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 */ /** @{ */ /** * 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 * * @param tile The initial tile of the player * @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 * int[MOVE_TILE_IDX (0)] tile after move * int[MOVE_INITTILE_IDX (1)] tile before move * int[MOVE_STEPS_IDX (2)] number of total steps for the move * 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:
    *    class MoveData {
    *       int tile;
    *       int initTile;
    *       ...
    *    }
    *    
* for returning value would be better, less error prone, cleaner, etc... * We could also had members like:
 MoveData previous; 
to help us further. * We kept this representation just because it was a requirement. */ int [] move (Board board, int tile, int roll, boolean run) { int t; Arrays.fill(dryMove, 0); dryMove[MOVE_INITTILE_IDX] = tile; dryMove[MOVE_ROLL_IDX] = roll; tile += roll; // Initial move boolean keepGoing; do { keepGoing = false; // Check apples //if ((t = board.checkApple(tile, run)) != 0) { if ((t = board.checkApple(tile, true)) != 0) { dryMove[MOVE_POINTS_IDX] += t; if (t > 0) ++dryMove[MOVE_RED_APPLES_IDX]; else ++dryMove[MOVE_BLACK_APPLES_IDX]; } // Check ladder //if ((t = board.checkLadder(tile, run)) != tile) { if ((t = board.checkLadder(tile, true)) != tile) { tile = t; ++dryMove[MOVE_LADDERS_IDX]; keepGoing = true; } // Check snakes if ((t = board.checkSnake(tile)) != tile) { tile = t; ++dryMove[MOVE_SNAKES_IDX]; keepGoing = true; } } while (keepGoing); 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 */ /** @{ */ int playerId; /**< Player's ID */ String name; /**< Player's name */ int score; /**< Player's score */ int tile; /**< Player's tile location */ int[] lastMove; /**< move() return data for statistics. These are only valid after a true move */ private int [] dryMove; /**< Fake (dry run) move return buffer */ /** @} */ }