Skip to content

Commit

Permalink
Add support for resigining and abandoning games
Browse files Browse the repository at this point in the history
  • Loading branch information
Sothatsit committed May 29, 2024
1 parent d7d823d commit 867cf22
Show file tree
Hide file tree
Showing 14 changed files with 742 additions and 144 deletions.
194 changes: 120 additions & 74 deletions src/main/java/net/royalur/Game.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import net.royalur.rules.simple.SimpleRuleSetProvider;
import net.royalur.rules.state.*;

import javax.annotation.Nullable;
import java.util.*;

/**
Expand Down Expand Up @@ -155,7 +156,7 @@ public List<GameState> getStates() {
* Retrieve the state that the game is currently in.
* @return The state that the game is currently in.
*/
public GameState getCurrentState() {
public GameState getState() {
return states.get(states.size() - 1);
}

Expand Down Expand Up @@ -188,40 +189,40 @@ public List<GameState> getLandmarkStates() {
* @return Whether the game is currently in a playable state.
*/
public boolean isPlayable() {
return getCurrentState() instanceof PlayableGameState;
return getState() instanceof PlayableGameState;
}

/**
* Determines whether the game is currently waiting for a roll from a player.
* @return Whether the game is currently waiting for a roll from a player.
*/
public boolean isWaitingForRoll() {
return getCurrentState() instanceof WaitingForRollGameState;
return getState() instanceof WaitingForRollGameState;
}

/**
* Determines whether the game is currently waiting for a move from a player.
* @return Whether the game is currently waiting for a move from a player.
*/
public boolean isWaitingForMove() {
return getCurrentState() instanceof WaitingForMoveGameState;
return getState() instanceof WaitingForMoveGameState;
}

/**
* Determines whether the game is currently in a finished state.
* @return Whether the game is currently in a finished state.
*/
public boolean isFinished() {
return getCurrentState() instanceof WinGameState;
return getState() instanceof EndGameState;
}

/**
* Gets the current state of this game as a {@link PlayableGameState}.
* This will throw an error if the game is not in a playable state.
* @return The playable state that the game is currently in.
*/
public PlayableGameState getCurrentPlayableState() {
GameState state = getCurrentState();
public PlayableGameState getPlayableState() {
GameState state = getState();
if (state instanceof PlayableGameState)
return (PlayableGameState) state;

Expand All @@ -233,8 +234,8 @@ public PlayableGameState getCurrentPlayableState() {
* This will throw an error if the game is not waiting for a roll from a player.
* @return The waiting for roll state that the game is currently in.
*/
public WaitingForRollGameState getCurrentWaitingForRollState() {
GameState state = getCurrentState();
public WaitingForRollGameState getWaitingForRollState() {
GameState state = getState();
if (state instanceof WaitingForRollGameState)
return (WaitingForRollGameState) state;

Expand All @@ -246,23 +247,23 @@ public WaitingForRollGameState getCurrentWaitingForRollState() {
* This will throw an error if the game is not waiting for a move from a player.
* @return The waiting for move state that the game is currently in.
*/
public WaitingForMoveGameState getCurrentWaitingForMoveState() {
GameState state = getCurrentState();
public WaitingForMoveGameState getWaitingForMoveState() {
GameState state = getState();
if (state instanceof WaitingForMoveGameState)
return (WaitingForMoveGameState) state;

throw new IllegalStateException("This game is not waiting for a move");
}

/**
* Gets the current state of this game as an instance of {@link WinGameState}.
* Gets the current state of this game as an instance of {@link EndGameState}.
* This will throw an error if the game has not ended.
* @return The win state that the game is currently in.
*/
public WinGameState getCurrentWinState() {
GameState state = getCurrentState();
if (state instanceof WinGameState)
return (WinGameState) state;
public EndGameState getEndState() {
GameState state = getState();
if (state instanceof EndGameState endState)
return endState;

throw new IllegalStateException("This game has not ended");
}
Expand All @@ -273,8 +274,7 @@ public WinGameState getCurrentWinState() {
* @param roll The value of the dice that is to be rolled.
*/
public void rollDice(Roll roll) {
WaitingForRollGameState state = getCurrentWaitingForRollState();
addStates(rules.applyRoll(state, roll));
addStates(rules.applyRoll(getWaitingForRollState(), roll));
}

/**
Expand Down Expand Up @@ -304,7 +304,32 @@ public Roll rollDice(int value) {
* @return All moves that can be made from the current position.
*/
public List<Move> findAvailableMoves() {
return getCurrentWaitingForMoveState().getAvailableMoves();
return getWaitingForMoveState().getAvailableMoves();
}

/**
* Finds the move of the piece {@code piece}.
* @param piece The piece to find the move for.
*/
public Move findMove(Piece piece) {
for (Move move : findAvailableMoves()) {
if (move.hasSource() && move.getSourcePiece().equals(piece))
return move;
}
throw new IllegalStateException("The piece cannot be moved, " + piece);
}

/**
* Finds the move of the piece on {@code tile}.
* @param sourceTile The tile of the piece to find the move for.
*/
public Move findMove(Tile sourceTile) {
PathPair paths = rules.getPaths();
for (Move move : findAvailableMoves()) {
if (move.getSource(paths).equals(sourceTile))
return move;
}
throw new IllegalStateException("The piece on " + sourceTile + " cannot be moved");
}

/**
Expand All @@ -313,78 +338,73 @@ public List<Move> findAvailableMoves() {
* @param move The move to make from the current state of the game.
*/
public void makeMove(Move move) {
WaitingForMoveGameState state = getCurrentWaitingForMoveState();
addStates(rules.applyMove(state, move));
addStates(rules.applyMove(getWaitingForMoveState(), move));
}

/**
* Moves the piece {@code piece}, and updates the state of the game.
* @param piece The piece to be moved.
*/
public void makeMove(Piece piece) {
for (Move move : findAvailableMoves()) {
if (!move.hasSource() || !move.getSourcePiece().equals(piece))
continue;

makeMove(move);
return;
}
throw new IllegalStateException("The piece cannot be moved, " + piece);
makeMove(findMove(piece));
}

/**
* Moves the piece on the given source tile, and updates the state of the game.
* @param sourceTile The tile where the piece to be moved resides.
*/
public void makeMove(Tile sourceTile) {
WaitingForMoveGameState state = getCurrentWaitingForMoveState();
PathPair paths = rules.getPaths();
for (Move move : findAvailableMoves()) {
if (!move.getSource(paths).equals(sourceTile))
continue;
makeMove(findMove(sourceTile));
}

makeMove(move);
return;
}
throw new IllegalStateException("There is no available move from " + sourceTile);
/**
* Marks that {@code player} resigned the game.
* @param player The player to resign the game.
*/
public void resign(PlayerType player) {
if (isFinished())
throw new IllegalStateException("The game is already finished");

addStates(rules.applyResign(getState(), player));
}

/**
* Moves a new piece onto the board.
* Marks that the game was abandoned due to {@code reason}. The person that
* abandoned the game can be provided using {@code player}, or {@code null}
* can be passed if a specific player did not abandon the game. For example,
* if a game had to end due to a venue closing, a player should not be provided.
* @param reason The reason the game was abandoned.
* @param player The player that abandoned the game, or {@code null}.
*/
public void makeMoveIntroducingPiece() {
for (Move move : findAvailableMoves()) {
if (!move.isIntroducingPiece())
continue;
public void abandon(AbandonReason reason, @Nullable PlayerType player) {
if (isFinished())
throw new IllegalStateException("The game is already finished");

makeMove(move);
return;
}
throw new IllegalStateException("There is no available move to introduce a piece to the board");
addStates(rules.applyAbandon(getState(), reason, player));
}

/**
* Gets the current state of the board.
* @return The current state of the board.
*/
public Board getBoard() {
return getCurrentState().getBoard();
return getState().getBoard();
}

/**
* Gets the current state of the light player.
* @return The current state of the light player.
*/
public PlayerState getLightPlayer() {
return getCurrentState().getLightPlayer();
return getState().getLightPlayer();
}

/**
* Gets the current state of the dark player.
* @return The current state of the dark player.
*/
public PlayerState getDarkPlayer() {
return getCurrentState().getDarkPlayer();
return getState().getDarkPlayer();
}

/**
Expand All @@ -393,79 +413,105 @@ public PlayerState getDarkPlayer() {
* @return The state of the player {@code player}.
*/
public PlayerState getPlayer(PlayerType player) {
return getCurrentState().getPlayer(player);
return getState().getPlayer(player);
}

/**
* Gets the player who can make the next interaction with the game.
* @return The player who can make the next interaction with the game.
*/
public PlayerType getTurn() {
return getCurrentPlayableState().getTurn();
return getPlayableState().getTurn();
}

/**
* Gets the player who can make the next interaction with the game,
* or the winner of the game if it is finished.
* @return The player who can make the next interaction with the game,
* or the winner of the game if it is finished.
* Gets the state of the player whose turn it is.
* @return The state of the player whose turn it is.
*/
public PlayerType getTurnOrWinner() {
if (isPlayable())
return getTurn();

if (isFinished())
return getWinner();

throw new IllegalStateException("The game is not in a playable or won state");
public PlayerState getTurnPlayer() {
return getPlayableState().getTurnPlayer();
}

/**
* Gets the state of the player whose turn it is.
* @return The state of the player whose turn it is.
* Gets the player that is waiting whilst the other player makes the
* next interaction with the game.
* @return The player who is waiting for the other player to interact
* with the game.
*/
public PlayerState getTurnPlayer() {
return getCurrentPlayableState().getTurnPlayer();
public PlayerType getWaiting() {
return getPlayableState().getWaiting();
}

/**
* Gets the state of the player that is waiting as it is not their turn.
* @return The state of the player that is waiting as it is not their turn.
*/
public PlayerState getWaitingPlayer() {
return getCurrentPlayableState().getWaitingPlayer();
return getPlayableState().getWaitingPlayer();
}

/**
* Gets whether this game has a winner.
* @return Whether this game has a winner.
*/
public boolean hasWinner() {
return isFinished() && getEndState().hasWinner();
}

/**
* Gets the player that won the game.
* @return The player that won the game.
*/
public PlayerType getWinner() {
return getCurrentWinState().getWinner();
return getEndState().getWinner();
}

/**
* Gets whether this game has a winner.
* @return Whether this game has a winner.
*/
public boolean hasLoser() {
return isFinished() && getEndState().hasLoser();
}

/**
* Gets the player that lost the game.
* @return The player that lost the game.
*/
public PlayerType getLoser() {
return getCurrentWinState().getLoser();
return getEndState().getLoser();
}

/**
* Gets the state of the winning player.
* @return The state of the winning player.
*/
public PlayerState getWinningPlayer() {
return getCurrentWinState().getWinningPlayer();
return getEndState().getWinningPlayer();
}

/**
* Gets the state of the losing player.
* @return The state of the losing player.
*/
public PlayerState getLosingPlayer() {
return getCurrentWinState().getLosingPlayer();
return getEndState().getLosingPlayer();
}

/**
* Gets the player who can make the next interaction with the game,
* or the winner of the game if it is finished.
* @return The player who can make the next interaction with the game,
* or the winner of the game if it is finished.
*/
public PlayerType getTurnOrWinner() {
if (isPlayable())
return getTurn();

if (isFinished())
return getWinner();

throw new IllegalStateException("The game is not playable or finished");
}

/**
Expand All @@ -474,7 +520,7 @@ public PlayerState getLosingPlayer() {
* @return The roll that was made that can now be used to make a move.
*/
public Roll getRoll() {
return getCurrentWaitingForMoveState().getRoll();
return getWaitingForMoveState().getRoll();
}

/**
Expand Down
Loading

0 comments on commit 867cf22

Please sign in to comment.