Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ public void handlePlayerMove() {

// Use mediator to notify next player
mediator.notify(this, "nextPlayer");
boardGame.notifyObservers(); // Notify observers after every move
}

/**
Expand Down Expand Up @@ -169,36 +170,30 @@ public MoveResult movePlayer(String playerName, int roll) {
String type = "normal";

// Ensure we don't go past the board size
int boardSize = boardGame.getBoard().getSizeOfBoard();
if (end >= boardSize) {
// In Snakes and Ladders, you must land exactly on the final tile
// If the roll would take you past it, you don't move
if (end == boardSize - 1) {
// Exact landing on final tile - allow the move
end = boardSize - 1;
} else {
// Overshoot - stay in current position
end = start;
return new MoveResult(start, end, type);
}
int lastTile = boardGame.getBoard().getSizeOfBoard();
if (end == lastTile) {
return new MoveResult(start, end - 1, "win");
}
if (end > lastTile) {
int overshoot = end - lastTile;
end = lastTile - overshoot;
}

int steps = end - start;
if (steps > 0) player.move(steps);
if (end != start) {
player.setCurrentTile(boardGame.getBoard().getTile(end));
}

// Check for snakes
if (tileConfig.isSnakeHead(end)) {
int tail = tileConfig.getSnakeTail(end);
steps = tail - end;
player.move(steps);
player.setCurrentTile(boardGame.getBoard().getTile(tail));
end = tail;
type = "snake";
}
// Check for ladders only if it's a normal move
else if (tileConfig.isLadderStart(end)) {
int top = tileConfig.getLadderEnd(end);
steps = top - end;
player.move(steps);
player.setCurrentTile(boardGame.getBoard().getTile(top));
end = top;
type = "ladder";
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,15 +440,15 @@ private void rollDiceAndMove() {
updatePlayerPosition(currentPlayer);

if (result.type.equals("snake")) {
String snakeMessage = currentPlayer + " hit a snake! Moving from " + result.start + " to " + result.end;
String snakeMessage = "🐍 " + currentPlayer + " hit a snake! Moving from " + result.start + " to " + result.end;
LOGGER.info(snakeMessage);
displaySnakeOrLadderMessage(currentPlayer, result.start, result.end, "snake");
// Add to history
addMessageToHistory(snakeMessage);
// Update position again after snake
updatePlayerPosition(currentPlayer);
} else if (result.type.equals("ladder")) {
String ladderMessage = currentPlayer + " hit a ladder! Moving from " + result.start + " to " + result.end;
String ladderMessage = "🪜 " + currentPlayer + " hit a ladder! Moving from " + result.start + " to " + result.end;
LOGGER.info(ladderMessage);
displaySnakeOrLadderMessage(currentPlayer, result.start, result.end, "ladder");
// Add to history
Expand All @@ -457,11 +457,11 @@ private void rollDiceAndMove() {
updatePlayerPosition(currentPlayer);
} else {
// Normal move
String moveMessage = currentPlayer + " moved from " + result.start + " to " + result.end;
String moveMessage = "🎲 " + currentPlayer + " moved from " + result.start + " to " + result.end;
addMessageToHistory(moveMessage);
}

if (result.end == 100) {
if (result.type.equals("win")) {
String winMessage = "🏆 " + currentPlayer + " WINS! 🏆";
statusLabel.setText(winMessage);
currentTurnLabel.setText(winMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,10 +121,10 @@ void testHandlePlayerMove_NormalMove() {
when(mockPlayer1.getCurrentPosition()).thenReturn(5);
when(mockTileConfig.isSnakeHead(8)).thenReturn(false);
when(mockTileConfig.isLadderStart(8)).thenReturn(false);
when(mockBoard.getTile(8)).thenReturn(mock(Tile.class));

// Should not throw and should notify next player
controller.handlePlayerMove();

verify(mockPlayer1).move(3);
verify(mockMediator).notify(controller, "nextPlayer");
}

Expand All @@ -136,11 +136,12 @@ void testHandlePlayerMove_PlayerWins() {
when(mockPlayer1.getCurrentPosition()).thenReturn(97);
when(mockTileConfig.isSnakeHead(100)).thenReturn(false);
when(mockTileConfig.isLadderStart(100)).thenReturn(false);
when(mockBoard.getTile(100)).thenReturn(mock(Tile.class));

// Call handlePlayerMove and verify mediator is NOT notified (since it's a win)
controller.handlePlayerMove();

verify(mockPlayer1).move(3);
verify(mockMediator, never()).notify(controller, "nextPlayer");
// The win type is already tested in testMovePlayer_ExactlyOnFinalTile
}

@Test
Expand Down Expand Up @@ -182,28 +183,31 @@ void testMovePlayer_NormalMove() {
when(mockPlayer1.getCurrentPosition()).thenReturn(10);
when(mockTileConfig.isSnakeHead(15)).thenReturn(false);
when(mockTileConfig.isLadderStart(15)).thenReturn(false);
when(mockBoard.getTile(15)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 5);

assertEquals(10, result.start);
assertEquals(15, result.end);
assertEquals("normal", result.type);
verify(mockPlayer1).move(5);
// No move() call, only setCurrentTile
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
void testMovePlayer_HitSnake() {
when(mockPlayer1.getCurrentPosition()).thenReturn(10);
when(mockTileConfig.isSnakeHead(25)).thenReturn(true);
when(mockTileConfig.getSnakeTail(25)).thenReturn(5);
when(mockBoard.getTile(25)).thenReturn(mock(Tile.class));
when(mockBoard.getTile(5)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 15);

assertEquals(10, result.start);
assertEquals(5, result.end);
assertEquals("snake", result.type);
verify(mockPlayer1).move(15); // Move to snake head
verify(mockPlayer1).move(-20); // Move down the snake
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
Expand All @@ -212,42 +216,58 @@ void testMovePlayer_ClimbLadder() {
when(mockTileConfig.isSnakeHead(20)).thenReturn(false);
when(mockTileConfig.isLadderStart(20)).thenReturn(true);
when(mockTileConfig.getLadderEnd(20)).thenReturn(35);
when(mockBoard.getTile(20)).thenReturn(mock(Tile.class));
when(mockBoard.getTile(35)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 10);

assertEquals(10, result.start);
assertEquals(35, result.end);
assertEquals("ladder", result.type);
verify(mockPlayer1).move(10); // Move to ladder start
verify(mockPlayer1).move(15); // Climb the ladder
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
void testMovePlayer_ExactlyOnFinalTile() {
when(mockPlayer1.getCurrentPosition()).thenReturn(97);
when(mockBoard.getSizeOfBoard()).thenReturn(101); // 0-100
when(mockBoard.getSizeOfBoard()).thenReturn(100); // 1-100
when(mockTileConfig.isSnakeHead(100)).thenReturn(false);
when(mockTileConfig.isLadderStart(100)).thenReturn(false);

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 3);

assertEquals(97, result.start);
assertEquals(100, result.end);
assertEquals("normal", result.type);
verify(mockPlayer1).move(3);
assertEquals(99, result.end); // Should be lastTile - 1 for win
assertEquals("win", result.type);
verify(mockPlayer1, never()).move(anyInt());
}

@Test
void testMovePlayer_OvershootFinalTile() {
when(mockPlayer1.getCurrentPosition()).thenReturn(97);
when(mockBoard.getSizeOfBoard()).thenReturn(101); // 0-100
when(mockBoard.getTile(100)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 5);

assertEquals(97, result.start);
assertEquals(97, result.end); // Should stay in place when overshooting
assertEquals(100, result.end); // Should bounce back to 100 (101- (97+5-101) = 100)
assertEquals("normal", result.type);
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
void testMovePlayer_BounceBackFromLastTile() {
when(mockPlayer1.getCurrentPosition()).thenReturn(99);
when(mockBoard.getSizeOfBoard()).thenReturn(101); // 0-100
when(mockBoard.getTile(98)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 5);

assertEquals(99, result.start);
assertEquals(98, result.end); // Should bounce back to 98 (99+5=104, bounce back to 98)
assertEquals("normal", result.type);
verify(mockPlayer1, never()).move(anyInt()); // No movement should occur
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
Expand Down Expand Up @@ -375,14 +395,15 @@ void testMovePlayer_ComplexScenario() {
when(mockPlayer1.getCurrentPosition()).thenReturn(90);
when(mockTileConfig.isSnakeHead(95)).thenReturn(true);
when(mockTileConfig.getSnakeTail(95)).thenReturn(75);
when(mockBoard.getTile(95)).thenReturn(mock(Tile.class));
when(mockBoard.getTile(75)).thenReturn(mock(Tile.class));

SnakesAndLaddersController.MoveResult result = controller.movePlayer("Player1", 5);

assertEquals(90, result.start);
assertEquals(75, result.end);
assertEquals("snake", result.type);
verify(mockPlayer1).move(5); // Move to the snake head
verify(mockPlayer1).move(-20); // Move down the snake
verify(mockPlayer1, atLeastOnce()).setCurrentTile(any());
}

@Test
Expand All @@ -402,10 +423,9 @@ void testHandlePlayerMove_Integration() {
when(mockPlayer1.getCurrentPosition()).thenReturn(10);
when(mockTileConfig.isSnakeHead(14)).thenReturn(false);
when(mockTileConfig.isLadderStart(14)).thenReturn(false);
when(mockBoard.getTile(14)).thenReturn(mock(Tile.class));

controller.handlePlayerMove();

verify(mockPlayer1).move(4);
verify(mockMediator).notify(controller, "nextPlayer");
}

Expand Down