I finished the Snakes and Ladders kata in the Agile Katas and I would like to know if there's a way to improve my code (specifically the part where the turn order is determined).
Game
public class Game {
private Map<Player, Token> playerTokenMap;
private Player winner;
private final int lastSquareLocation = 101;
private Space[] board;
private final Player computer = new Player("COMPUTER");
private LinkedList<Player> turnOrder;
private int turnNumber;
public Game(SpecialEnds specialEnds) {
playerTokenMap = new HashMap<Player, Token>();
turnOrder = new LinkedList<Player>();
board = new Space[lastSquareLocation];
for(int squareLocation = 1; squareLocation < lastSquareLocation; squareLocation++){
Space space = new Space();
if(specialEnds.leadsToAnotherSquareLocation(squareLocation))
space.setDestination(specialEnds.getDestination(squareLocation));
board[squareLocation] = space;
}
this.winner = Player.NO_ONE;
}
public void addPlayer(Player player) {
turnOrder.clear();
playerTokenMap.put(player, new Token());
}
public int getPositionOfTokenOf(Player tokenOwner) {
return playerTokenMap.get(tokenOwner).getLocation();
}
public void turnPlayerMove(int spaces) {
Player turnPlayer = getTurnPlayer();
Token token = playerTokenMap.get(turnPlayer);
if(tokenWillGoPastTheLastSquare(token, spaces))
return;
token.move(spaces);
int tokenLocation = token.getLocation();
if(hasSpecialEnd(tokenLocation))
token.move(this.board[tokenLocation].getDestination() - tokenLocation);
if(hasWonTheGame(token))
this.winner = turnPlayer;
turnNumber++;
}
private boolean hasSpecialEnd(int squareLocationOfToken) {
return board[squareLocationOfToken].isYieldingToAnotherDestination();
}
private boolean hasWonTheGame(Token token) {
return token.getLocation() == lastSquareLocation - 1;
}
private boolean tokenWillGoPastTheLastSquare(Token token, int numberOfSpacesToMove) {
return token.getLocation() + numberOfSpacesToMove > lastSquareLocation - 1;
}
public Player getWinner() {
return winner;
}
public Player getTurnPlayer() {
if(turnOrder.isEmpty())
return Player.NO_ONE;
return turnOrder.get(turnNumber % playerTokenMap.size());
}
public void determinePlayOrder() {
SortedMap<Integer, Player> playOrderPlayerMap = new TreeMap<Integer, Player>();
for(Player player : playerTokenMap.keySet()){
int dieRollResult = player.rollDice();
if(!playOrderPlayerMap.containsKey(dieRollResult))
playOrderPlayerMap.put(dieRollResult, player);
else{
while(playOrderPlayerMap.containsKey(dieRollResult)){
dieRollResult = player.rollDice();
}
playOrderPlayerMap.put(dieRollResult, player);
}
}
turnOrder.addAll(playOrderPlayerMap.values());
Collections.reverse(turnOrder);
}
public void addComputerToGame() {
addPlayer(computer);
}
}
Space
public class Space {
private int destination;
public Space() {
this.destination = -1;
}
public void setDestination(int destination) {
this.destination = destination;
}
public boolean isYieldingToAnotherDestination() {
return destination != -1;
}
public int getDestination() {
return destination;
}
}
SpecialEnds
public class SpecialEnds {
private Map<Integer, Integer> mapOfSpecialEnds;
public SpecialEnds() {
mapOfSpecialEnds = new HashMap<Integer, Integer>();
}
public void add(int source, int destination){
mapOfSpecialEnds.put(source, destination);
}
public boolean leadsToAnotherSquareLocation(int squareLocation) {
return mapOfSpecialEnds.containsKey(squareLocation);
}
public int getDestination(int squareLocation) {
return mapOfSpecialEnds.get(squareLocation);
}
}
GameTest
public class GameTest {
private Game game;
private Player player1;
@Before
public void setUp() throws Exception {
SpecialEnds specialEnds = new SpecialEnds();
specialEnds.add(12, 2);
specialEnds.add(3, 13);
game = new Game(specialEnds);
player1 = new Player("Rome");
game.addPlayer(player1);
game.determinePlayOrder();
}
@Test
public void tokenShouldStartOnSquareOne() throws Exception {
int expectedSquareLocation = 1;
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void tokenShouldBeInSquare4AfterMoving3SpacesFromSquare1() throws Exception {
int expectedSquareLocation = 4;
int numberOfSpacesToMove = 3;
turnPlayerMove(numberOfSpacesToMove);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void tokenShouldBeInSquare8AfterMoving3SpacesThen4SpacesFromSquare1() throws Exception {
int expectedSquareLocation = 8;
int firstNumberOfSpacesToMove = 3;
int secondNumberOfSpacesToMove = 4;
turnPlayerMove(firstNumberOfSpacesToMove);
turnPlayerMove(secondNumberOfSpacesToMove);
assertPlayerIsIn(player1, expectedSquareLocation);
}
private void turnPlayerMove(int spaces) {
game.turnPlayerMove(spaces);
}
@Test
public void numberOfMovesShouldBeDecidedByDieRolls() throws Exception {
int dieResult = 1;
int expectedSquareLocation = 5;
while (dieResult != 4) {
dieResult = player1.rollDice();
}
turnPlayerMove(dieResult);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void playerShouldWinTheGameWhenTheirTokenReachesThe100thSquare() throws Exception {
turnPlayerMove(96);
int numberOfSpacesToMove = 3;
turnPlayerMove(numberOfSpacesToMove);
assertEquals(player1, game.getWinner());
}
@Test
public void playerShouldNotYetWinTheGameWhenTheirTokenMovementWillExceedThe100thSquare() throws Exception {
turnPlayerMove(96);
int numberOfSpacesToMove = 4;
turnPlayerMove(numberOfSpacesToMove);
assertNotEquals(player1, game.getWinner());
}
@Test
public void tokenThatLandsOnASnakeHeadShouldGoDownOnTheTail() throws Exception {
int expectedSquareLocation = 2;
turnPlayerMove(11);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void tokenThatLandsOnASnakeTailShouldStayThere() throws Exception {
int expectedSquareLocation = 2;
turnPlayerMove(1);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void tokenThatLandsOnLowerEndOfTheLadderShouldGoUpTheLadder() throws Exception {
int expectedSquareLocation = 13;
turnPlayerMove(2);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void tokenThatLandsOnUpperEndOfTheLadderShouldStayThere() throws Exception {
int expectedSquareLocation = 13;
turnPlayerMove(12);
assertPlayerIsIn(player1, expectedSquareLocation);
}
@Test
public void player1ShouldBeFirstIfPlayer1RollsHigherThanPlayer2() throws Exception {
Player player2 = new Player("Ein");
game.addPlayer(player2);
while (isNotFirstToRoll(player1))
game.determinePlayOrder();
assertTurnPlayerIs(player1);
}
@Test
public void player2ShouldBeFirstIfPlayer2RollsHigherThanPlayer1() throws Exception {
Player player2 = new Player("Ein");
game.addPlayer(player2);
while (isNotFirstToRoll(player2))
game.determinePlayOrder();
assertTurnPlayerIs(player2);
}
private void assertTurnPlayerIs(Player player2) {
assertEquals(player2, game.getTurnPlayer());
}
@Test
public void player2ShouldGoNextAfterPlayer1() throws Exception {
Player player2 = new Player("Ein");
game.addPlayer(player2);
while (isNotFirstToRoll(player1))
game.determinePlayOrder();
turnPlayerMove(2);
assertTurnPlayerIs(player2);
}
@Test
public void player1ShouldGoNextAfterPlayer2GoingAfterPlayer1() throws Exception {
Player player2 = new Player("Ein");
game.addPlayer(player2);
while (isNotFirstToRoll(player1))
game.determinePlayOrder();
turnPlayerMove(2);
turnPlayerMove(2);
assertTurnPlayerIs(player1);
}
private boolean isNotFirstToRoll(Player player) {
return !player.equals(game.getTurnPlayer());
}
@Test
public void computerShouldGoFirstIfComputerRollsHigherThanPlayer1() throws Exception {
Player expectedTurnPlayer = new Player("COMPUTER");
game.addComputerToGame();
while (isNotFirstToRoll(expectedTurnPlayer))
game.determinePlayOrder();
assertTurnPlayerIs(expectedTurnPlayer);
}
@Test
public void computerShouldBeAbleToRollTheDie() throws Exception {
int expectedSquareLocation = 2;
Player computer = new Player("COMPUTER");
game.addComputerToGame();
while (isNotFirstToRoll(computer))
game.determinePlayOrder();
turnPlayerMove(1);
assertPlayerIsIn(computer, expectedSquareLocation);
}
private void assertPlayerIsIn(Player computer, int expectedSquareLocation) {
assertTrue(game.getPositionOfTokenOf(computer) == expectedSquareLocation);
}
}