Feat/Game Engine #233

Merged
j.kropff merged 18 commits from feat/game-engine into main 2026-04-05 11:26:12 +02:00
41 changed files with 623 additions and 822 deletions
Showing only changes of commit bf7ac4fc34 - Show all commits
@@ -17,9 +17,8 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* GameController is responsible for managing the flow of the poker game. It * GameController is responsible for managing the flow of the poker game. It interacts with the
* interacts with the GameEngine to process player actions and update the game * GameEngine to process player actions and update the game state accordingly.
* state accordingly.
*/ */
public class GameController { public class GameController {
@@ -48,7 +47,7 @@ public class GameController {
/** /**
* Adds a player to the game with the specified name and initial chip count. * Adds a player to the game with the specified name and initial chip count.
* *
* @param name The name of the player to add. * @param name The name of the player to add.
* @param chips The initial number of chips the player has. * @param chips The initial number of chips the player has.
*/ */
public void addPlayer(PlayerId name, int chips) { public void addPlayer(PlayerId name, int chips) {
@@ -58,14 +57,13 @@ public class GameController {
// players.add(id); // players.add(id);
// engine.getState().addPlayer(id, chips); // engine.getState().addPlayer(id, chips);
players.add(name); players.add(name);
engine.getState().addPlayer(name, chips); engine.getState().addPlayer(name, chips);
} }
/** /**
* Initializes a new hand by preparing the deck, setting the phase to PREFLOP, * Initializes a new hand by preparing the deck, setting the phase to PREFLOP, rotating the
* rotating the dealer, dealing hole cards, posting blinds, * dealer, dealing hole cards, posting blinds, and setting the first active player.
* and setting the first active player.
*/ */
public void startGame() { public void startGame() {
@@ -86,9 +84,7 @@ public class GameController {
engine.getState().setCurrentPlayerIndex(nextPlayerIndex); engine.getState().setCurrentPlayerIndex(nextPlayerIndex);
} }
/** /** Rotates the dealer position to the next player in the list. */
* Rotates the dealer position to the next player in the list.
*/
private void rotateDealer() { private void rotateDealer() {
dealerIndex = (dealerIndex + DEALER_OFFSET) % players.size(); dealerIndex = (dealerIndex + DEALER_OFFSET) % players.size();
} }
@@ -103,8 +99,8 @@ public class GameController {
} }
/** /**
* Determines the small and big blind players relative to the dealer * Determines the small and big blind players relative to the dealer and submits the
* and submits the corresponding blind actions to the engine. * corresponding blind actions to the engine.
*/ */
public void postBlinds() { public void postBlinds() {
@@ -115,9 +111,7 @@ public class GameController {
engine.processAction(new BlindAction(bigBlind, BIG_BLIND)); engine.processAction(new BlindAction(bigBlind, BIG_BLIND));
} }
/** /** Deals hole cards to each player from the deck. */
* Deals hole cards to each player from the deck.
*/
public void dealHoleCards() { public void dealHoleCards() {
Deck deck = engine.getState().getDeck(); Deck deck = engine.getState().getDeck();
@@ -131,9 +125,7 @@ public class GameController {
} }
} }
/** /** Draws three cards from the deck and adds them as community cards (flop). */
* Draws three cards from the deck and adds them as community cards (flop).
*/
public void dealFlop() { public void dealFlop() {
Deck deck = engine.getState().getDeck(); Deck deck = engine.getState().getDeck();
@@ -144,16 +136,14 @@ public class GameController {
} }
/** /**
* Deals the turn by drawing one community card from the deck and adding it to * Deals the turn by drawing one community card from the deck and adding it to the game state.
* the game state.
*/ */
public void dealTurn() { public void dealTurn() {
engine.getState().addCommunityCard(engine.getState().getDeck().draw()); engine.getState().addCommunityCard(engine.getState().getDeck().draw());
} }
/** /**
* Deals the river by drawing one community card from the deck and adding it to * Deals the river by drawing one community card from the deck and adding it to the game state.
* the game state.
*/ */
public void dealRiver() { public void dealRiver() {
engine.getState().addCommunityCard(engine.getState().getDeck().draw()); engine.getState().addCommunityCard(engine.getState().getDeck().draw());
@@ -183,7 +173,7 @@ public class GameController {
* Processes a player's raise action by sending a RaiseAction to the GameEngine. * Processes a player's raise action by sending a RaiseAction to the GameEngine.
* *
* @param playerId The ID of the player who is raising. * @param playerId The ID of the player who is raising.
* @param amount The amount the player is raising. * @param amount The amount the player is raising.
*/ */
public void playerRaise(PlayerId playerId, int amount) { public void playerRaise(PlayerId playerId, int amount) {
// engine.processAction(new RaiseAction(PlayerId.of(playerId), amount)); // engine.processAction(new RaiseAction(PlayerId.of(playerId), amount));
@@ -211,19 +201,19 @@ public class GameController {
/** /**
* Retrieves the hole cards for each player in the game. * Retrieves the hole cards for each player in the game.
* *
* @return A map where the key is the player's name and the value is a list of * @return A map where the key is the player's name and the value is a list of Card objects
* Card objects representing the player's hole cards. * representing the player's hole cards.
*/ */
public Map<PlayerId, List<Card>> getPlayerCards() { public Map<PlayerId, List<Card>> getPlayerCards() {
return engine.getState().getPlayerCards(); return engine.getState().getPlayerCards();
} }
/** /**
* Determines the winner of the current hand by evaluating the best possible * Determines the winner of the current hand by evaluating the best possible poker hand for each
* poker hand for each active player. * active player.
* *
* The evaluation is based on the player's two hole cards combined with the * <p>The evaluation is based on the player's two hole cards combined with the five community
* five community cards on the board. * cards on the board.
* *
* @return The ID of the winning player. * @return The ID of the winning player.
*/ */
@@ -3,10 +3,9 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
/** /**
* AbstractAction serves as a base class for all player actions in the poker * AbstractAction serves as a base class for all player actions in the poker game. It implements the
* game. * Action interface and provides a common implementation for retrieving the player ID associated
* It implements the Action interface and provides a common implementation for * with the action.
* retrieving the player ID associated with the action.
*/ */
public abstract class AbstractAction implements Action { public abstract class AbstractAction implements Action {
protected final PlayerId playerId; protected final PlayerId playerId;
@@ -1,13 +1,12 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The Action interface defines the structure for all player actions in the * The Action interface defines the structure for all player actions in the poker game. Each action
* poker game. Each action must specify its type, provide an implementation for * must specify its type, provide an implementation for executing the action on the game state, and
* executing the action on the game state, and identify the player performing * identify the player performing the action.
* the action.
*/ */
public interface Action { public interface Action {
ActionType getType(); ActionType getType();
@@ -1,9 +1,9 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action;
/** /**
* ActionType is an enumeration that defines the various types of actions that * ActionType is an enumeration that defines the various types of actions that players can perform
* players can perform in a poker game. Each action type corresponds to a * in a poker game. Each action type corresponds to a specific move or decision that a player can
* specific move or decision that a player can make during their turn. * make during their turn.
*/ */
public enum ActionType { public enum ActionType {
FOLD, FOLD,
@@ -5,11 +5,9 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* AllInAction represents the action of a player going all-in in a poker game. * AllInAction represents the action of a player going all-in in a poker game. When a player goes
* When * all-in, they bet all of their remaining chips. This action updates the player's chip count, adds
* a player goes all-in, they bet all of their remaining chips. This action * the bet to the pot, and updates the current bet for the player in the game state.
* updates the player's chip count, adds the bet to the pot, and updates the
* current bet for the player in the game state.
*/ */
public class AllInAction extends AbstractAction { public class AllInAction extends AbstractAction {
@@ -33,9 +31,8 @@ public class AllInAction extends AbstractAction {
} }
/** /**
* Executes the all-in action on the given game state. This method updates the * Executes the all-in action on the given game state. This method updates the player's chip
* player's chip count, adds the bet to the pot, and updates the current bet * count, adds the bet to the pot, and updates the current bet for the player in the game state.
* for the player in the game state.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
*/ */
@@ -5,9 +5,9 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* Represents a bet action where a player contributes a fixed amount of chips. * Represents a bet action where a player contributes a fixed amount of chips. The amount is
* The amount is deducted from the player's chips, added to the pot, and both * deducted from the player's chips, added to the pot, and both the player's current bet and the
* the player's current bet and the table's current bet are set to this amount. * table's current bet are set to this amount.
*/ */
public class BetAction extends AbstractAction { public class BetAction extends AbstractAction {
@@ -17,7 +17,7 @@ public class BetAction extends AbstractAction {
* Constructs a BetAction for the specified player ID and bet amount. * Constructs a BetAction for the specified player ID and bet amount.
* *
* @param playerId The ID of the player performing the bet action. * @param playerId The ID of the player performing the bet action.
* @param amount The amount of chips the player is betting. * @param amount The amount of chips the player is betting.
*/ */
public BetAction(PlayerId playerId, int amount) { public BetAction(PlayerId playerId, int amount) {
super(playerId); super(playerId);
@@ -44,9 +44,8 @@ public class BetAction extends AbstractAction {
} }
/** /**
* Executes the bet action on the given game state. This method updates the * Executes the bet action on the given game state. This method updates the player's chip count,
* player's chip count, adds the bet to the pot, and updates the current bet * adds the bet to the pot, and updates the current bet for the player in the game state.
* for the player in the game state.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
*/ */
@@ -5,9 +5,8 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* Executes a blind by deducting chips from the player, adding them to the pot, * Executes a blind by deducting chips from the player, adding them to the pot, increasing the
* increasing the player's current bet, and updating the table's current bet * player's current bet, and updating the table's current bet if the blind exceeds the existing bet.
* if the blind exceeds the existing bet.
*/ */
public class BlindAction implements Action { public class BlindAction implements Action {
@@ -18,7 +17,7 @@ public class BlindAction implements Action {
* Constructs a BlindAction for the specified player ID and blind amount. * Constructs a BlindAction for the specified player ID and blind amount.
* *
* @param playerId The ID of the player posting the blind bet. * @param playerId The ID of the player posting the blind bet.
* @param amount The amount of chips the player is posting as a blind bet. * @param amount The amount of chips the player is posting as a blind bet.
*/ */
public BlindAction(PlayerId playerId, int amount) { public BlindAction(PlayerId playerId, int amount) {
this.playerId = playerId; this.playerId = playerId;
@@ -41,14 +40,14 @@ public class BlindAction implements Action {
* @return The player ID associated with this action. * @return The player ID associated with this action.
*/ */
@Override @Override
public PlayerId getPlayerId() { public PlayerId getPlayerId() {
return playerId; return playerId;
} }
/** /**
* Executes the blind action on the given game state. This method updates the * Executes the blind action on the given game state. This method updates the player's chip
* player's chip count, adds the blind bet to the pot, and updates the current * count, adds the blind bet to the pot, and updates the current bet for the player in the game
* bet for the player in the game state. * state.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
*/ */
@@ -5,11 +5,10 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* CallAction represents the action of a player calling in a poker game. When a * CallAction represents the action of a player calling in a poker game. When a player calls, they
* player calls, they match the current bet on the table by paying the * match the current bet on the table by paying the difference between their current bet and the
* difference between their current bet and the table's current bet. This action * table's current bet. This action updates the player's chip count, adds the required amount to the
* updates the player's chip count, adds the required amount to the pot, and * pot, and updates the player's current bet accordingly.
* updates the player's current bet accordingly.
*/ */
public class CallAction extends AbstractAction { public class CallAction extends AbstractAction {
@@ -33,10 +32,9 @@ public class CallAction extends AbstractAction {
} }
/** /**
* Executes the call action on the given game state. This method checks if * Executes the call action on the given game state. This method checks if the player is folded,
* the player is folded, calculates the amount to call, updates the player's * calculates the amount to call, updates the player's chip count, adds the amount to the pot,
* chip count, adds the amount to the pot, and updates the player's current * and updates the player's current bet.
* bet.
* *
* @param state the current game state on which to execute the call action * @param state the current game state on which to execute the call action
*/ */
@@ -5,10 +5,9 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* FoldAction represents the action of a player folding in a poker game. When a * FoldAction represents the action of a player folding in a poker game. When a player folds, they
* player folds, they forfeit their hand and are no longer active in the current * forfeit their hand and are no longer active in the current round. This action updates the
* round. This action updates the player's status to indicate that they have * player's status to indicate that they have folded.
* folded.
*/ */
public class FoldAction extends AbstractAction { public class FoldAction extends AbstractAction {
@@ -32,8 +31,8 @@ public class FoldAction extends AbstractAction {
} }
/** /**
* Executes the fold action on the given game state. This method updates the * Executes the fold action on the given game state. This method updates the player's status to
* player's status to indicate that they have folded. * indicate that they have folded.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
*/ */
@@ -11,7 +11,7 @@ public class RaiseAction extends AbstractAction {
/** /**
* Constructs a RaiseAction for the specified player ID and raise amount. * Constructs a RaiseAction for the specified player ID and raise amount.
* *
* @param playerId The ID of the player performing the raise action. * @param playerId The ID of the player performing the raise action.
* @param raiseAmount The amount of chips the player is raising. * @param raiseAmount The amount of chips the player is raising.
*/ */
public RaiseAction(PlayerId playerId, int raiseAmount) { public RaiseAction(PlayerId playerId, int raiseAmount) {
@@ -39,9 +39,9 @@ public class RaiseAction extends AbstractAction {
} }
/** /**
* Executes the raise action on the given game state. This method updates the * Executes the raise action on the given game state. This method updates the player's chip
* player's chip count, adds the raise amount to the pot, and updates the * count, adds the raise amount to the pot, and updates the current bet for the player in the
* current bet for the player in the game state. * game state.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
*/ */
@@ -1,11 +1,10 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
/** /**
* The Card class represents a single playing card in a standard deck of cards. * The Card class represents a single playing card in a standard deck of cards. Each card has a suit
* Each card has a suit (Hearts, Diamonds, Clubs, Spades) and a rank (2-10, * (Hearts, Diamonds, Clubs, Spades) and a rank (2-10, Jack, Queen, King, Ace). This class provides
* Jack, Queen, King, Ace). * methods to retrieve the suit and rank of the card, as well as a string representation of the
* This class provides methods to retrieve the suit and rank of the card, as * card.
* well as a string representation of the card.
*/ */
public class Card { public class Card {
@@ -6,20 +6,18 @@ import java.util.LinkedList;
import java.util.List; import java.util.List;
/** /**
* The Deck class represents a standard deck of playing cards. It provides * The Deck class represents a standard deck of playing cards. It provides functionality to create a
* functionality to create a full deck of 52 cards, shuffle the deck, and draw * full deck of 52 cards, shuffle the deck, and draw cards from it. The deck is initialized with all
* cards from it. The deck is initialized with all combinations of suits and * combinations of suits and ranks, and can be manipulated through its methods.
* ranks, and can be manipulated through its methods.
*/ */
public class Deck { public class Deck {
private List<Card> cards; private List<Card> cards;
/** /**
* Constructs a new Deck object and initializes it with a standard set of 52 * Constructs a new Deck object and initializes it with a standard set of 52 playing cards. The
* playing cards. The deck is created by iterating through all suits and ranks, * deck is created by iterating through all suits and ranks, creating a Card object for each
* creating a Card object for each combination, and adding it to the list of * combination, and adding it to the list of cards.
* cards.
*/ */
public Deck() { public Deck() {
@@ -33,19 +31,18 @@ public class Deck {
} }
/** /**
* Shuffles the deck of cards using the Collections.shuffle method, which * Shuffles the deck of cards using the Collections.shuffle method, which randomly permutes the
* randomly permutes the list of cards. This method can be called to randomize * list of cards. This method can be called to randomize the order of the cards in the deck
* the order of the cards in the deck before drawing. * before drawing.
*/ */
public void shuffle() { public void shuffle() {
Collections.shuffle(cards); Collections.shuffle(cards);
} }
/** /**
* Draws and removes the last card in the list, which represents the top of the deck. * Draws and removes the last card in the list, which represents the top of the deck. This
* This method removes and returns the last card in the list of cards, which * method removes and returns the last card in the list of cards, which represents the top of
* represents the top of the deck. If the deck is empty, this method will throw an * the deck. If the deck is empty, this method will throw an IndexOutOfBoundsException.
* IndexOutOfBoundsException.
* *
* @return The Card object that was drawn from the deck. * @return The Card object that was drawn from the deck.
*/ */
@@ -54,9 +51,9 @@ public class Deck {
} }
/** /**
* Retrieves the current list of cards in the deck. This method returns a new * Retrieves the current list of cards in the deck. This method returns a new LinkedList
* LinkedList containing the cards, which allows for safe manipulation of the * containing the cards, which allows for safe manipulation of the returned list without
* returned list without affecting the internal state of the deck. * affecting the internal state of the deck.
* *
* @return A List of Card objects currently in the deck. * @return A List of Card objects currently in the deck.
*/ */
@@ -1,9 +1,9 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
/** /**
* The Rank enumeration represents the rank of a playing card in a standard * The Rank enumeration represents the rank of a playing card in a standard deck of cards. It
* deck of cards. It includes the ranks from Two to Ace, which are used to * includes the ranks from Two to Ace, which are used to determine the value of the card in various
* determine the value of the card in various card games, including poker. * card games, including poker.
*/ */
public enum Rank { public enum Rank {
TWO, TWO,
@@ -1,10 +1,9 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
/** /**
* The Suit enumeration represents the four suits of a standard deck of playing * The Suit enumeration represents the four suits of a standard deck of playing cards: Hearts,
* cards: Hearts, Diamonds, Clubs, and Spades. Each suit is used to categorize * Diamonds, Clubs, and Spades. Each suit is used to categorize the cards and can have different
* the cards and can have different values in various card games, including * values in various card games, including poker.
* poker.
*/ */
public enum Suit { public enum Suit {
HEARTS, HEARTS,
@@ -5,12 +5,11 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The GameEngine class is responsible for managing the core logic of the poker * The GameEngine class is responsible for managing the core logic of the poker game. It integrates
* game. It integrates the RuleEngine, RoundManager, and TurnManager to * the RuleEngine, RoundManager, and TurnManager to orchestrate the flow of the game. The GameEngine
* orchestrate the flow of the game. The GameEngine processes player actions, * processes player actions, updates the game state accordingly, and ensures that the game
* updates the game state accordingly, and ensures that the game progresses * progresses through its various stages (pre-flop, flop, turn, river) while adhering to the rules
* through its various stages (pre-flop, flop, turn, river) while adhering to * of poker.
* the rules of poker.
*/ */
public class GameEngine { public class GameEngine {
@@ -21,18 +20,17 @@ public class GameEngine {
private final GameState state; private final GameState state;
/** /**
* Constructs a GameEngine with the specified game state, rule engine, round * Constructs a GameEngine with the specified game state, rule engine, round manager, and turn
* manager, and turn manager. * manager.
* *
* @param state The initial game state to be managed by the engine. * @param state The initial game state to be managed by the engine.
* @param ruleEngine The RuleEngine instance responsible for validating player * @param ruleEngine The RuleEngine instance responsible for validating player actions.
* actions. * @param roundManager The RoundManager instance responsible for managing the progression of
* @param roundManager The RoundManager instance responsible for managing the * rounds.
* progression of rounds. * @param turnManager The TurnManager instance responsible for managing player turns.
* @param turnManager The TurnManager instance responsible for managing player
* turns.
*/ */
public GameEngine(GameState state, public GameEngine(
GameState state,
RuleEngine ruleEngine, RuleEngine ruleEngine,
RoundManager roundManager, RoundManager roundManager,
TurnManager turnManager) { TurnManager turnManager) {
@@ -43,8 +41,8 @@ public class GameEngine {
} }
/** /**
* Starts a new hand with the provided game state. This method initializes the * Starts a new hand with the provided game state. This method initializes the round manager to
* round manager to begin a new hand and sets up the game state accordingly. * begin a new hand and sets up the game state accordingly.
* *
* @param state The game state to be used for starting the new hand. * @param state The game state to be used for starting the new hand.
*/ */
@@ -53,17 +51,17 @@ public class GameEngine {
} }
/** /**
* Starts a new hand using the current game state. This method is a convenience * Starts a new hand using the current game state. This method is a convenience method that
* method that calls the startNewHand method with the current state of the game. * calls the startNewHand method with the current state of the game.
*/ */
public void startNewHand() { public void startNewHand() {
roundManager.startNewHand(state); roundManager.startNewHand(state);
} }
/** /**
* Processes a player action by validating it against the game rules, executing * Processes a player action by validating it against the game rules, executing the action,
* the action, progressing to the next player's turn, and checking if the * progressing to the next player's turn, and checking if the round should progress to the next
* round should progress to the next stage. * stage.
* *
* @param action The player action to be processed. * @param action The player action to be processed.
*/ */
@@ -72,14 +70,12 @@ public class GameEngine {
} }
/** /**
* Handles a player action by performing the following steps: * Handles a player action by performing the following steps: 1. Validates the action against
* 1. Validates the action against the game rules using the RuleEngine. * the game rules using the RuleEngine. 2. Executes the action, which updates the game state
* 2. Executes the action, which updates the game state accordingly. * accordingly. 3. Advances to the next player's turn using the TurnManager. 4. Checks if the
* 3. Advances to the next player's turn using the TurnManager. * round should progress to the next stage (e.g., from pre-flop to flop) using the RoundManager.
* 4. Checks if the round should progress to the next stage (e.g., from pre-flop
* to flop) using the RoundManager.
* *
* @param state The current game state on which to execute the action. * @param state The current game state on which to execute the action.
* @param action The player action to be processed. * @param action The player action to be processed.
*/ */
public void handleAction(GameState state, Action action) { public void handleAction(GameState state, Action action) {
@@ -7,11 +7,10 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* RoundManager is responsible for managing the flow of a poker game round. It * RoundManager is responsible for managing the flow of a poker game round. It handles the
* handles the progression of the game through its various phases (pre-flop, * progression of the game through its various phases (pre-flop, flop, turn, river) and manages
* flop, turn, river) and manages player actions such as posting blinds and * player actions such as posting blinds and dealing cards. The RoundManager ensures that the game
* dealing cards. The RoundManager ensures that the game state is updated * state is updated correctly based on player actions and the current phase of the game.
* correctly based on player actions and the current phase of the game.
*/ */
public class RoundManager { public class RoundManager {
@@ -19,9 +18,9 @@ public class RoundManager {
public static final int BIG_BLIND = 200; public static final int BIG_BLIND = 200;
/** /**
* Starts a new hand by initializing the game state, dealing cards to players, * Starts a new hand by initializing the game state, dealing cards to players, and posting
* and posting blinds. This method sets the hand as active and resets any * blinds. This method sets the hand as active and resets any necessary state variables to
* necessary state variables to prepare for a new round of play. * prepare for a new round of play.
* *
* @param state The game state to be used for starting the new hand. * @param state The game state to be used for starting the new hand.
*/ */
@@ -35,13 +34,12 @@ public class RoundManager {
} }
/** /**
* Checks if the betting round is finished and advances the game phase if * Checks if the betting round is finished and advances the game phase if necessary. This method
* necessary. This method evaluates the current bets of all active players and * evaluates the current bets of all active players and determines if the betting round can be
* determines if the betting round can be concluded. If all players have met * concluded. If all players have met the current bet or are all-in, the game phase is advanced
* the current bet or are all-in, the game phase is advanced to the next stage. * to the next stage.
* *
* @param state The current game state to be evaluated for betting round * @param state The current game state to be evaluated for betting round progression.
* progression.
*/ */
public void progressIfNeeded(GameState state) { public void progressIfNeeded(GameState state) {
@@ -51,13 +49,11 @@ public class RoundManager {
} }
/** /**
* Determines if the betting round is finished by checking if all active players * Determines if the betting round is finished by checking if all active players have met the
* have met the current bet or are all-in. This method iterates through all * current bet or are all-in. This method iterates through all players in the game state and
* players in the game state and evaluates their bets against the current bet on * evaluates their bets against the current bet on the table.
* the table.
* *
* @param state The current game state to be evaluated for betting round * @param state The current game state to be evaluated for betting round completion.
* completion.
* @return true if the betting round is finished, false otherwise. * @return true if the betting round is finished, false otherwise.
*/ */
private boolean isBettingRoundFinished(GameState state) { private boolean isBettingRoundFinished(GameState state) {
@@ -82,10 +78,9 @@ public class RoundManager {
} }
/** /**
* Advances the game phase to the next stage (flop, turn, river, or showdown) * Advances the game phase to the next stage (flop, turn, river, or showdown) based on the
* based on the current phase of the game. This method is called when the * current phase of the game. This method is called when the betting round is finished and
* betting round is finished and updates the game state accordingly to reflect * updates the game state accordingly to reflect the new phase of play.
* the new phase of play.
* *
* @param state The current game state to be updated with the new phase. * @param state The current game state to be updated with the new phase.
*/ */
@@ -100,10 +95,9 @@ public class RoundManager {
} }
/** /**
* Handles the posting of blinds at the start of a new hand. This method * Handles the posting of blinds at the start of a new hand. This method identifies the players
* identifies the players responsible for posting the small and big blinds, * responsible for posting the small and big blinds, updates their chip counts, adds the blind
* updates their chip counts, adds the blind amounts to the pot, and updates * amounts to the pot, and updates the current bets for those players in the game state.
* the current bets for those players in the game state.
* *
* @param state The current game state to be updated with the posted blinds. * @param state The current game state to be updated with the posted blinds.
*/ */
@@ -127,18 +121,13 @@ public class RoundManager {
state.getTableState().setCurrentBet(bigBlind); state.getTableState().setCurrentBet(bigBlind);
} }
private void dealCards(GameState state) { private void dealCards(GameState state) {}
}
private void dealFlop(GameState state) { private void dealFlop(GameState state) {}
}
private void dealTurn(GameState state) { private void dealTurn(GameState state) {}
}
private void dealRiver(GameState state) { private void dealRiver(GameState state) {}
}
private void showdown(GameState state) { private void showdown(GameState state) {}
}
} }
@@ -3,28 +3,23 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The TurnManager class is responsible for managing the flow of turns in a * The TurnManager class is responsible for managing the flow of turns in a poker game. It provides
* poker * functionality to determine the next player in the sequence and update the game state accordingly.
* game. It provides functionality to determine the next player in the sequence * The TurnManager ensures that players take their turns in the correct order, allowing for a smooth
* and update the game state accordingly. The TurnManager ensures that players * and organized gameplay experience.
* take their turns in the correct order, allowing for a smooth and organized
* gameplay experience.
*/ */
public class TurnManager { public class TurnManager {
/** /**
* Advances the game state to the next player's turn. This method calculates * Advances the game state to the next player's turn. This method calculates the index of the
* the index of the next player based on the current player index and the total * next player based on the current player index and the total number of players in the game. It
* number of players in the game. It then updates the game state to reflect * then updates the game state to reflect the new current player.
* the new current player.
* *
* @param state The current game state that will be updated to reflect the next * @param state The current game state that will be updated to reflect the next player's turn.
* player's turn.
*/ */
public void nextPlayer(GameState state) { public void nextPlayer(GameState state) {
int next = (state.getCurrentPlayerIndex() + 1) int next = (state.getCurrentPlayerIndex() + 1) % state.getPlayers().size();
% state.getPlayers().size();
state.setCurrentPlayerIndex(next); state.setCurrentPlayerIndex(next);
} }
@@ -10,13 +10,11 @@ import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
* The HandEvaluator class provides functionality to evaluate a poker hand and * The HandEvaluator class provides functionality to evaluate a poker hand and determine its rank.
* determine its rank. It analyzes a list of cards and identifies the best * It analyzes a list of cards and identifies the best possible hand according to standard poker
* possible hand according to standard poker rules, such as flushes, straights, * rules, such as flushes, straights, pairs, and more. The evaluation process involves counting card
* pairs, and more. The evaluation process involves counting card ranks, * ranks, grouping by suits, and checking for specific hand combinations to assign the correct hand
* grouping * rank.
* by suits, and checking for specific hand combinations to assign the correct
* hand rank.
*/ */
public class HandEvaluator { public class HandEvaluator {
private static final int RANK_OFFSET = 2; private static final int RANK_OFFSET = 2;
@@ -97,9 +95,7 @@ public class HandEvaluator {
return onePair; return onePair;
} }
return new HandRank( return new HandRank(HandRank.Type.HIGH_CARD, ranks.stream().limit(HAND_SIZE).toList());
HandRank.Type.HIGH_CARD,
ranks.stream().limit(HAND_SIZE).toList());
} }
/** /**
@@ -119,38 +115,32 @@ public class HandEvaluator {
* Helper method to count the occurrences of each card rank in the hand. * Helper method to count the occurrences of each card rank in the hand.
* *
* @param ranks A list of integer ranks representing the cards in the hand. * @param ranks A list of integer ranks representing the cards in the hand.
* @return A map where the key is the card rank and the value is the count of * @return A map where the key is the card rank and the value is the count of occurrences.
* occurrences.
*/ */
private static Map<Integer, Long> getRankCount(List<Integer> ranks) { private static Map<Integer, Long> getRankCount(List<Integer> ranks) {
return ranks.stream() return ranks.stream().collect(Collectors.groupingBy(r -> r, Collectors.counting()));
.collect(Collectors.groupingBy(r -> r, Collectors.counting()));
} }
/** /**
* Helper method to group the cards by their suits. * Helper method to group the cards by their suits.
* *
* @param cards A list of Card objects representing the player's hand. * @param cards A list of Card objects representing the player's hand.
* @return A map where the key is the Suit and the value is a list of Cards * @return A map where the key is the Suit and the value is a list of Cards belonging to that
* belonging to that suit. * suit.
*/ */
private static Map<Suit, List<Card>> groupBySuit(List<Card> cards) { private static Map<Suit, List<Card>> groupBySuit(List<Card> cards) {
return cards.stream() return cards.stream().collect(Collectors.groupingBy(Card::getSuit));
.collect(Collectors.groupingBy(Card::getSuit));
} }
/** /**
* Helper method to identify if there is a flush in the hand and return the * Helper method to identify if there is a flush in the hand and return the corresponding cards.
* corresponding cards.
* *
* @param suits A map where the key is the Suit and the value is a list of Cards * @param suits A map where the key is the Suit and the value is a list of Cards belonging to
* belonging to that suit. * that suit.
* @return A list of Card objects that form a flush, or null if no flush is * @return A list of Card objects that form a flush, or null if no flush is found.
* found.
*/ */
private static List<Card> getFlushCards(Map<Suit, List<Card>> suits) { private static List<Card> getFlushCards(Map<Suit, List<Card>> suits) {
return suits.values() return suits.values().stream()
.stream()
.filter(s -> s.size() >= MIN_FLUSH_SIZE) .filter(s -> s.size() >= MIN_FLUSH_SIZE)
.findFirst() .findFirst()
.orElse(null); .orElse(null);
@@ -163,11 +153,12 @@ public class HandEvaluator {
* @return A HandRank object representing the flush hand rank. * @return A HandRank object representing the flush hand rank.
*/ */
private static HandRank buildFlush(List<Card> flushCards) { private static HandRank buildFlush(List<Card> flushCards) {
List<Integer> flushRanks = flushCards.stream() List<Integer> flushRanks =
.map(c -> c.getRank().ordinal() + CARD_VALUE_OFFSET) flushCards.stream()
.sorted(Comparator.reverseOrder()) .map(c -> c.getRank().ordinal() + CARD_VALUE_OFFSET)
.limit(HAND_SIZE) .sorted(Comparator.reverseOrder())
.toList(); .limit(HAND_SIZE)
.toList();
return new HandRank(HandRank.Type.FLUSH, flushRanks); return new HandRank(HandRank.Type.FLUSH, flushRanks);
} }
@@ -176,29 +167,31 @@ public class HandEvaluator {
* Helper method to check for a straight flush or royal flush in the hand. * Helper method to check for a straight flush or royal flush in the hand.
* *
* @param flushCards A list of Card objects that form a flush. * @param flushCards A list of Card objects that form a flush.
* @return A HandRank object representing the straight flush or royal flush hand * @return A HandRank object representing the straight flush or royal flush hand rank, or null
* rank, or null if neither is found. * if neither is found.
*/ */
private static HandRank checkStraightFlush(List<Card> flushCards) { private static HandRank checkStraightFlush(List<Card> flushCards) {
if (flushCards == null) { if (flushCards == null) {
return null; return null;
} }
List<Integer> flushRanks = flushCards.stream() List<Integer> flushRanks =
.map(c -> c.getRank().ordinal() + RANK_OFFSET) flushCards.stream()
.sorted(Comparator.reverseOrder()) .map(c -> c.getRank().ordinal() + RANK_OFFSET)
.toList(); .sorted(Comparator.reverseOrder())
.toList();
if (!isStraight(flushRanks)) { if (!isStraight(flushRanks)) {
return null; return null;
} }
if (flushRanks.containsAll(List.of( if (flushRanks.containsAll(
ROYAL_FLUSH_ACE, List.of(
ROYAL_FLUSH_KING, ROYAL_FLUSH_ACE,
ROYAL_FLUSH_QUEEN, ROYAL_FLUSH_KING,
ROYAL_FLUSH_JACK, ROYAL_FLUSH_QUEEN,
ROYAL_FLUSH_TEN))) { ROYAL_FLUSH_JACK,
ROYAL_FLUSH_TEN))) {
return new HandRank(HandRank.Type.ROYAL_FLUSH, flushRanks); return new HandRank(HandRank.Type.ROYAL_FLUSH, flushRanks);
} }
@@ -209,10 +202,9 @@ public class HandEvaluator {
/** /**
* Helper method to check for a four of a kind hand rank. * Helper method to check for a four of a kind hand rank.
* *
* @param rankCount A map where the key is the card rank and the value is the * @param rankCount A map where the key is the card rank and the value is the count of
* count of occurrences. * occurrences.
* @return A HandRank object representing the four of a kind hand rank, or null * @return A HandRank object representing the four of a kind hand rank, or null if not found.
* if not found.
*/ */
private static HandRank checkFourOfAKind(Map<Integer, Long> rankCount) { private static HandRank checkFourOfAKind(Map<Integer, Long> rankCount) {
if (!rankCount.containsValue(FOUR_OF_A_KIND_COUNT)) { if (!rankCount.containsValue(FOUR_OF_A_KIND_COUNT)) {
@@ -221,34 +213,31 @@ public class HandEvaluator {
int quad = getRank(rankCount, FOUR_OF_A_KIND); int quad = getRank(rankCount, FOUR_OF_A_KIND);
return new HandRank( return new HandRank(HandRank.Type.FOUR_OF_A_KIND, List.of(quad));
HandRank.Type.FOUR_OF_A_KIND,
List.of(quad));
} }
/** /**
* Helper method to check for a full house hand rank. * Helper method to check for a full house hand rank.
* *
* @param rankCount A map where the key is the card rank and the value is the * @param rankCount A map where the key is the card rank and the value is the count of
* count of occurrences. * occurrences.
* @return A HandRank object representing the full house hand rank, or null if * @return A HandRank object representing the full house hand rank, or null if not found.
* not found.
*/ */
private static HandRank checkFullHouse(Map<Integer, Long> rankCount) { private static HandRank checkFullHouse(Map<Integer, Long> rankCount) {
List<Integer> trips = rankCount.entrySet() List<Integer> trips =
.stream() rankCount.entrySet().stream()
.filter(e -> e.getValue() >= THREE_OF_A_KIND) .filter(e -> e.getValue() >= THREE_OF_A_KIND)
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.sorted(Comparator.reverseOrder()) .sorted(Comparator.reverseOrder())
.toList(); .toList();
List<Integer> pairs = rankCount.entrySet() List<Integer> pairs =
.stream() rankCount.entrySet().stream()
.filter(e -> e.getValue() >= PAIR) .filter(e -> e.getValue() >= PAIR)
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.sorted(Comparator.reverseOrder()) .sorted(Comparator.reverseOrder())
.toList(); .toList();
if (trips.isEmpty()) { if (trips.isEmpty()) {
return null; return null;
@@ -256,27 +245,21 @@ public class HandEvaluator {
int three = trips.get(FIRST_INDEX); int three = trips.get(FIRST_INDEX);
Integer pair = pairs.stream() Integer pair = pairs.stream().filter(p -> p != three).findFirst().orElse(null);
.filter(p -> p != three)
.findFirst()
.orElse(null);
if (pair == null) { if (pair == null) {
return null; return null;
} }
return new HandRank( return new HandRank(HandRank.Type.FULL_HOUSE, List.of(three, pair));
HandRank.Type.FULL_HOUSE,
List.of(three, pair));
} }
/** /**
* Helper method to check for a three of a kind hand rank. * Helper method to check for a three of a kind hand rank.
* *
* @param rankCount A map where the key is the card rank and the value is the * @param rankCount A map where the key is the card rank and the value is the count of
* count of occurrences. * occurrences.
* @return A HandRank object representing the three of a kind hand rank, or null * @return A HandRank object representing the three of a kind hand rank, or null if not found.
* if not found.
*/ */
private static HandRank checkThreeOfAKind(Map<Integer, Long> rankCount) { private static HandRank checkThreeOfAKind(Map<Integer, Long> rankCount) {
if (!rankCount.containsValue(THREE_OF_A_KIND_COUNT)) { if (!rankCount.containsValue(THREE_OF_A_KIND_COUNT)) {
@@ -285,57 +268,45 @@ public class HandEvaluator {
int tripsRank = getRank(rankCount, THREE_OF_A_KIND_RANK); int tripsRank = getRank(rankCount, THREE_OF_A_KIND_RANK);
return new HandRank( return new HandRank(HandRank.Type.THREE_OF_A_KIND, List.of(tripsRank));
HandRank.Type.THREE_OF_A_KIND,
List.of(tripsRank));
} }
/** /**
* Helper method to check for a two pair hand rank. * Helper method to check for a two pair hand rank.
* *
* @param rankCount A map where the key is the card rank and the value is the * @param rankCount A map where the key is the card rank and the value is the count of
* count of occurrences. * occurrences.
* @return A HandRank object representing the two pair hand rank, or null if * @return A HandRank object representing the two pair hand rank, or null if not found.
* not found.
*/ */
private static HandRank checkTwoPair(Map<Integer, Long> rankCount) { private static HandRank checkTwoPair(Map<Integer, Long> rankCount) {
long pairCount = rankCount.values() long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count();
.stream()
.filter(v -> v == PAIR)
.count();
if (pairCount < PAIR) { if (pairCount < PAIR) {
return null; return null;
} }
List<Integer> pairRanks = rankCount.entrySet() List<Integer> pairRanks =
.stream() rankCount.entrySet().stream()
.filter(e -> e.getValue() == PAIR) .filter(e -> e.getValue() == PAIR)
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.sorted(Comparator.reverseOrder()) .sorted(Comparator.reverseOrder())
.limit(PAIR) .limit(PAIR)
.toList(); .toList();
return new HandRank( return new HandRank(HandRank.Type.TWO_PAIR, pairRanks);
HandRank.Type.TWO_PAIR,
pairRanks);
} }
/** /**
* Helper method to check for a one pair hand rank. * Helper method to check for a one pair hand rank.
* *
* @param rankCount A map where the key is the card rank and the value is the * @param rankCount A map where the key is the card rank and the value is the count of
* count of occurrences. * occurrences.
* @return A HandRank object representing the one pair hand rank, or null if * @return A HandRank object representing the one pair hand rank, or null if not found.
* not found.
*/ */
private static HandRank checkOnePair(Map<Integer, Long> rankCount) { private static HandRank checkOnePair(Map<Integer, Long> rankCount) {
long pairCount = rankCount.values() long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count();
.stream()
.filter(v -> v == PAIR)
.count();
if (pairCount != ONE_PAIR) { if (pairCount != ONE_PAIR) {
return null; return null;
@@ -343,26 +314,19 @@ public class HandEvaluator {
int pair = getRank(rankCount, PAIR); int pair = getRank(rankCount, PAIR);
return new HandRank( return new HandRank(HandRank.Type.ONE_PAIR, List.of(pair));
HandRank.Type.ONE_PAIR,
List.of(pair));
} }
/** /**
* Helper method to get the rank of a card based on the count of occurrences in * Helper method to get the rank of a card based on the count of occurrences in the hand.
* the hand.
* *
* @param map A map where the key is the card rank and the value is the count * @param map A map where the key is the card rank and the value is the count of occurrences.
* of occurrences. * @param count The specific count to look for (e.g., 2 for pairs, 3 for three of a kind).
* @param count The specific count to look for (e.g., 2 for pairs, 3 for three * @return The rank of the card that matches the specified count, or 0 if none found.
* of a kind).
* @return The rank of the card that matches the specified count, or 0 if none
* found.
*/ */
private static int getRank(Map<Integer, Long> map, int count) { private static int getRank(Map<Integer, Long> map, int count) {
return map.entrySet() return map.entrySet().stream()
.stream()
.filter(e -> e.getValue() == count) .filter(e -> e.getValue() == count)
.map(Map.Entry::getKey) .map(Map.Entry::getKey)
.max(Integer::compare) .max(Integer::compare)
@@ -380,12 +344,13 @@ public class HandEvaluator {
Set<Integer> unique = new HashSet<>(ranks); Set<Integer> unique = new HashSet<>(ranks);
// Wheel Straight A-2-3-4-5 // Wheel Straight A-2-3-4-5
if (unique.containsAll(List.of( if (unique.containsAll(
ROYAL_FLUSH_ACE, List.of(
ROYAL_FLUSH_KING, ROYAL_FLUSH_ACE,
ROYAL_FLUSH_QUEEN, ROYAL_FLUSH_KING,
ROYAL_FLUSH_JACK, ROYAL_FLUSH_QUEEN,
ROYAL_FLUSH_TEN))) { ROYAL_FLUSH_JACK,
ROYAL_FLUSH_TEN))) {
return true; return true;
} }
@@ -3,20 +3,17 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator;
import java.util.List; import java.util.List;
/** /**
* HandRank represents the rank of a poker hand, including its type (e.g., * HandRank represents the rank of a poker hand, including its type (e.g., flush, straight) and the
* flush, * kickers used for tie-breaking. It implements the Comparable interface to allow for easy
* straight) and the kickers used for tie-breaking. It implements the * comparison between different hand ranks.
* Comparable interface to allow for easy comparison between different hand
* ranks.
*/ */
public class HandRank implements Comparable<HandRank> { public class HandRank implements Comparable<HandRank> {
/** /**
* The Type enumeration defines the different types of poker hands, each with * The Type enumeration defines the different types of poker hands, each with an associated
* an associated strength value for comparison purposes. * strength value for comparison purposes.
*/ */
public enum Type { public enum Type {
HIGH_CARD(1), HIGH_CARD(1),
ONE_PAIR(2), ONE_PAIR(2),
TWO_PAIR(3), TWO_PAIR(3),
@@ -45,7 +42,7 @@ public class HandRank implements Comparable<HandRank> {
/** /**
* Constructs a HandRank with the specified type and kickers. * Constructs a HandRank with the specified type and kickers.
* *
* @param type The type of the hand (e.g., flush, straight). * @param type The type of the hand (e.g., flush, straight).
* @param kickers A list of integers representing the kickers for tie-breaking. * @param kickers A list of integers representing the kickers for tie-breaking.
*/ */
public HandRank(Type type, List<Integer> kickers) { public HandRank(Type type, List<Integer> kickers) {
@@ -72,19 +69,17 @@ public class HandRank implements Comparable<HandRank> {
} }
/** /**
* Compares this HandRank with another HandRank for ordering. The comparison is * Compares this HandRank with another HandRank for ordering. The comparison is based first on
* based first on the type of the hand and then on the kickers for tie-breaking. * the type of the hand and then on the kickers for tie-breaking.
* *
* @param other The other HandRank to compare against. * @param other The other HandRank to compare against.
* @return A negative integer, zero, or a positive integer as this HandRank is * @return A negative integer, zero, or a positive integer as this HandRank is less than, equal
* less than, equal to, or greater than the specified HandRank. * to, or greater than the specified HandRank.
*/ */
@Override @Override
public int compareTo(HandRank other) { public int compareTo(HandRank other) {
int typeCompare = Integer.compare( int typeCompare = Integer.compare(this.type.getStrength(), other.type.getStrength());
this.type.getStrength(),
other.type.getStrength());
if (typeCompare != 0) { if (typeCompare != 0) {
return typeCompare; return typeCompare;
@@ -92,9 +87,7 @@ public class HandRank implements Comparable<HandRank> {
for (int i = 0; i < Math.min(kickers.size(), other.kickers.size()); i++) { for (int i = 0; i < Math.min(kickers.size(), other.kickers.size()); i++) {
int cmp = Integer.compare( int cmp = Integer.compare(kickers.get(i), other.kickers.get(i));
kickers.get(i),
other.kickers.get(i));
if (cmp != 0) { if (cmp != 0) {
return cmp; return cmp;
@@ -5,10 +5,9 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* The Player class represents a participant in the poker game. It holds * The Player class represents a participant in the poker game. It holds information about the
* information about the player's identity, chip count, status, and hand of * player's identity, chip count, status, and hand of cards. The class provides methods for managing
* cards. The class provides methods for managing the player's chips, hand, and * the player's chips, hand, and status during the game.
* status during the game.
*/ */
public class Player { public class Player {
@@ -17,13 +16,13 @@ public class Player {
private PlayerStatus status; private PlayerStatus status;
private boolean folded = false; private boolean folded = false;
private List<Card> hand = new ArrayList<>(); private List<Card> hand = new ArrayList<>();
/** /**
* Constructs a Player with the specified ID and initial chip count. The * Constructs a Player with the specified ID and initial chip count. The player's status is set
* player's status is set to ACTIVE by default. * to ACTIVE by default.
* *
* @param id The unique identifier for the player. * @param id The unique identifier for the player.
* @param chips The initial number of chips the player has. * @param chips The initial number of chips the player has.
*/ */
public Player(PlayerId id, int chips) { public Player(PlayerId id, int chips) {
@@ -51,8 +50,7 @@ public class Player {
} }
/** /**
* Returns the display name of the player. * Returns the display name of the player. Currently identical to the player ID.
* Currently identical to the player ID.
* *
* @return The player's name. * @return The player's name.
*/ */
@@ -70,8 +68,8 @@ public class Player {
} }
/** /**
* Removes a specified amount of chips from the player's total. If the amount * Removes a specified amount of chips from the player's total. If the amount exceeds the
* exceeds the player's current chips, it sets the chip count to zero. * player's current chips, it sets the chip count to zero.
* *
* @param amount The number of chips to remove from the player. * @param amount The number of chips to remove from the player.
*/ */
@@ -127,9 +125,7 @@ public class Player {
hand.add(card); hand.add(card);
} }
/** /** Clears the player's hand of cards, removing all cards from the hand. */
* Clears the player's hand of cards, removing all cards from the hand.
*/
public void clearHand() { public void clearHand() {
hand.clear(); hand.clear();
} }
@@ -3,16 +3,16 @@ package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player;
import java.util.Objects; import java.util.Objects;
/** /**
* PlayerId is a value object that represents the unique identifier of a player * PlayerId is a value object that represents the unique identifier of a player in the game. It
* in the game. It encapsulates a string value and provides validation to ensure * encapsulates a string value and provides validation to ensure that it is not null. The PlayerId
* that it is not null. The PlayerId class also includes a factory method for * class also includes a factory method for creating instances and overrides the toString method for
* creating instances and overrides the toString method for easy representation. * easy representation.
*/ */
public record PlayerId(String value) { public record PlayerId(String value) {
/** /**
* Constructs a PlayerId with the specified value. The constructor validates * Constructs a PlayerId with the specified value. The constructor validates that the value is
* that the value is not null. * not null.
* *
* @param value the string value representing the player's unique identifier * @param value the string value representing the player's unique identifier
* @throws NullPointerException if the value is null * @throws NullPointerException if the value is null
@@ -32,8 +32,7 @@ public record PlayerId(String value) {
} }
/** /**
* Returns the string representation of the PlayerId, which is the encapsulated * Returns the string representation of the PlayerId, which is the encapsulated value.
* value.
* *
* @return the string value of the PlayerId * @return the string value of the PlayerId
*/ */
@@ -1,10 +1,9 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player;
/** /**
* The PlayerStatus enumeration represents the various states a player can be in * The PlayerStatus enumeration represents the various states a player can be in during a poker
* during a poker game. It helps to track the player's current status, such as * game. It helps to track the player's current status, such as whether they are actively
* whether they are actively participating in the hand, have folded, are all-in, * participating in the hand, have folded, are all-in, or have been eliminated from the game.
* or have been eliminated from the game.
*/ */
public enum PlayerStatus { public enum PlayerStatus {
ACTIVE, ACTIVE,
@@ -4,11 +4,10 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The Rule interface defines the structure for all rules in the poker game. * The Rule interface defines the structure for all rules in the poker game. Each rule must provide
* Each rule must provide a validate method that checks if a given action is * a validate method that checks if a given action is valid based on the current game state. If the
* valid based on the current game state. If the action violates the rule, a * action violates the rule, a RuleViolationException should be thrown to indicate the specific rule
* RuleViolationException should be thrown to indicate the specific rule that * that was violated.
* was violated.
*/ */
public interface Rule { public interface Rule {
void validate(GameState state, Action action); void validate(GameState state, Action action);
@@ -5,12 +5,10 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
import java.util.List; import java.util.List;
/** /**
* The RuleEngine class is responsible for managing and validating a list of * The RuleEngine class is responsible for managing and validating a list of rules in the poker
* rules in the poker game. It provides a method to validate a given action * game. It provides a method to validate a given action against all the rules, ensuring that the
* against all the rules, ensuring that the action complies with the game's * action complies with the game's regulations. If any rule is violated during validation, a
* regulations. If any rule is violated during validation, a * RuleViolationException will be thrown, indicating the specific rule that was not followed.
* RuleViolationException
* will be thrown, indicating the specific rule that was not followed.
*/ */
public class RuleEngine { public class RuleEngine {
@@ -26,11 +24,10 @@ public class RuleEngine {
} }
/** /**
* Validates the given action against all the rules in the RuleEngine. If any * Validates the given action against all the rules in the RuleEngine. If any rule is violated,
* rule is violated, a RuleViolationException will be thrown, indicating the * a RuleViolationException will be thrown, indicating the specific rule that was not followed.
* specific rule that was not followed.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated against the rules. * @param action The action to be validated against the rules.
* @throws RuleViolationException if any rule is violated during validation. * @throws RuleViolationException if any rule is violated during validation.
*/ */
@@ -1,18 +1,17 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules;
/** /**
* RuleViolationException is a custom exception that is thrown when a player * RuleViolationException is a custom exception that is thrown when a player action violates a
* action violates a specific rule in the poker game. This exception provides * specific rule in the poker game. This exception provides a message detailing the nature of the
* a message detailing the nature of the rule violation, allowing for better * rule violation, allowing for better error handling and user feedback when invalid actions are
* error handling and user feedback when invalid actions are attempted. * attempted.
*/ */
public class RuleViolationException extends RuntimeException { public class RuleViolationException extends RuntimeException {
/** /**
* Constructs a new RuleViolationException with the specified detail message. * Constructs a new RuleViolationException with the specified detail message.
* *
* @param message The detail message explaining the reason for the rule * @param message The detail message explaining the reason for the rule violation.
* violation.
*/ */
public RuleViolationException(String message) { public RuleViolationException(String message) {
super(message); super(message);
@@ -7,20 +7,18 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The AcceptedActionRule class implements the Rule interface and defines the * The AcceptedActionRule class implements the Rule interface and defines the validation logic for
* validation logic for accepted player actions in a poker game. It checks * accepted player actions in a poker game. It checks whether the action type is one of the allowed
* whether the action type is one of the allowed types (FOLD, CALL, RAISE, * types (FOLD, CALL, RAISE, CHECK, BET, ALL_IN) and throws a RuleViolationException if the action
* CHECK, BET, ALL_IN) and throws a RuleViolationException if the action is not * is not allowed.
* allowed.
*/ */
public class AcceptedActionRule implements Rule { public class AcceptedActionRule implements Rule {
/** /**
* Validates the given action against the accepted action types for a poker * Validates the given action against the accepted action types for a poker game. If the action
* game. If the action type is not one of the allowed types, a * type is not one of the allowed types, a RuleViolationException is thrown.
* RuleViolationException is thrown.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated. * @param action The action to be validated.
* @throws RuleViolationException if the action type is not allowed. * @throws RuleViolationException if the action type is not allowed.
*/ */
@@ -30,8 +28,7 @@ public class AcceptedActionRule implements Rule {
ActionType type = action.getType(); ActionType type = action.getType();
switch (type) { switch (type) {
case FOLD, CALL, RAISE, CHECK, BET, ALL_IN -> { case FOLD, CALL, RAISE, CHECK, BET, ALL_IN -> {}
}
default -> throw new RuleViolationException("Action not allowed"); default -> throw new RuleViolationException("Action not allowed");
} }
} }
@@ -10,20 +10,19 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* The ActionOrderRule class implements the Rule interface and defines the * The ActionOrderRule class implements the Rule interface and defines the validation logic for
* validation logic for ensuring that players take their turns in the correct * ensuring that players take their turns in the correct order during a poker game. It checks
* order during a poker game. It checks whether the player performing the action * whether the player performing the action is the current player whose turn it is, and throws a
* is the current player whose turn it is, and throws a RuleViolationException * RuleViolationException if the action is attempted out of turn.
* if the action is attempted out of turn.
*/ */
public class ActionOrderRule implements Rule { public class ActionOrderRule implements Rule {
/** /**
* Validates that the player performing the action is the current player * Validates that the player performing the action is the current player whose turn it is in the
* whose turn it is in the game. If the action is attempted by a player who * game. If the action is attempted by a player who is not the current player, a
* is not the current player, a RuleViolationException is thrown. * RuleViolationException is thrown.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated. * @param action The action to be validated.
* @throws RuleViolationException if the action is attempted out of turn. * @throws RuleViolationException if the action is attempted out of turn.
*/ */
@@ -8,21 +8,18 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The AllInRule class implements the Rule interface and defines the validation * The AllInRule class implements the Rule interface and defines the validation logic for the
* logic for the "all-in" action in a poker game. It checks whether the player * "all-in" action in a poker game. It checks whether the player attempting to go all-in has any
* attempting to go all-in has any chips left, and throws a * chips left, and throws a RuleViolationException if the player has no chips to bet.
* RuleViolationException
* if the player has no chips to bet.
*/ */
public class AllInRule implements Rule { public class AllInRule implements Rule {
/** /**
* Validates the "all-in" action by checking if the player has any chips left. * Validates the "all-in" action by checking if the player has any chips left. If the player has
* If the player has no chips, a RuleViolationException is thrown. * no chips, a RuleViolationException is thrown.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated, which should be an instance of * @param action The action to be validated, which should be an instance of AllInAction.
* AllInAction.
* @throws RuleViolationException if the player has no chips to go all-in. * @throws RuleViolationException if the player has no chips to go all-in.
*/ */
@Override @Override
@@ -7,25 +7,21 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The BindingDeclarationRule class implements the Rule interface and defines * The BindingDeclarationRule class implements the Rule interface and defines the validation logic
* the * for binding declarations in a poker game. It checks whether a raise action is an under-raise or
* validation logic for binding declarations in a poker game. It checks whether * string bet by comparing the raise amount to the current bet commitment of the player. If the
* a raise action is an under-raise or string bet by comparing the raise amount * raise amount is less than the committed amount, a RuleViolationException is thrown.
* to the current bet commitment of the player. If the raise amount is less than
* the committed amount, a RuleViolationException is thrown.
*/ */
public class BindingDeclarationRule implements Rule { public class BindingDeclarationRule implements Rule {
/** /**
* Validates the raise action by checking if the raise amount is less than the * Validates the raise action by checking if the raise amount is less than the current bet
* current bet commitment of the player. If it is, a RuleViolationException is * commitment of the player. If it is, a RuleViolationException is thrown, indicating an illegal
* thrown, indicating an illegal under-raise or string bet. * under-raise or string bet.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated, which should be an instance of * @param action The action to be validated, which should be an instance of RaiseAction.
* RaiseAction. * @throws RuleViolationException if the raise amount is less than the current bet commitment.
* @throws RuleViolationException if the raise amount is less than the current
* bet commitment.
*/ */
@Override @Override
public void validate(GameState state, Action action) { public void validate(GameState state, Action action) {
@@ -6,20 +6,19 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The HandActiveRule class implements the Rule interface and defines the * The HandActiveRule class implements the Rule interface and defines the validation logic for
* validation logic for checking if there is an active hand in a poker game. * checking if there is an active hand in a poker game. It ensures that players can only perform
* It ensures that players can only perform actions when there is an active * actions when there is an active hand, and throws a RuleViolationException if there is no active
* hand, * hand.
* and throws a RuleViolationException if there is no active hand.
*/ */
public class HandActiveRule implements Rule { public class HandActiveRule implements Rule {
/** /**
* Validates that there is an active hand in the game. If there is no active * Validates that there is an active hand in the game. If there is no active hand, a
* hand, a RuleViolationException is thrown, indicating that players cannot * RuleViolationException is thrown, indicating that players cannot perform actions without an
* perform actions without an active hand. * active hand.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated. * @param action The action to be validated.
* @throws RuleViolationException if there is no active hand in the game. * @throws RuleViolationException if there is no active hand in the game.
*/ */
@@ -7,22 +7,19 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The MinimumBetRule class implements the Rule interface and defines the * The MinimumBetRule class implements the Rule interface and defines the validation logic for
* validation logic for ensuring that a player's bet meets the minimum bet * ensuring that a player's bet meets the minimum bet requirement in a poker game. It checks if the
* requirement in a poker game. It checks if the bet amount is less than the * bet amount is less than the minimum bet (usually determined by the big blind) and throws a
* minimum bet (usually determined by the big blind) and throws a
* RuleViolationException if the bet is below the minimum. * RuleViolationException if the bet is below the minimum.
*/ */
public class MinimumBetRule implements Rule { public class MinimumBetRule implements Rule {
/** /**
* Validates the bet action by checking if the bet amount is less than the * Validates the bet action by checking if the bet amount is less than the minimum bet. If it
* minimum bet. If it is, a RuleViolationException is thrown, indicating that * is, a RuleViolationException is thrown, indicating that the bet is below the minimum.
* the bet is below the minimum.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated, which should be an instance of * @param action The action to be validated, which should be an instance of BetAction.
* BetAction.
* @throws RuleViolationException if the bet amount is below the minimum bet. * @throws RuleViolationException if the bet amount is below the minimum bet.
*/ */
@Override @Override
@@ -7,24 +7,21 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The MinimumRaiseRule class implements the Rule interface and defines the * The MinimumRaiseRule class implements the Rule interface and defines the validation logic for
* validation logic for ensuring that a raise action in a poker game meets the * ensuring that a raise action in a poker game meets the minimum raise requirement. It checks if
* minimum raise requirement. It checks if the amount of the raise is less than * the amount of the raise is less than the minimum raise defined in the game state, and if so, it
* the minimum raise defined in the game state, and if so, it throws a * throws a RuleViolationException indicating that the raise is too small.
* RuleViolationException indicating that the raise is too small.
*/ */
public class MinimumRaiseRule implements Rule { public class MinimumRaiseRule implements Rule {
/** /**
* Validates the raise action by checking if the raise amount is less than the * Validates the raise action by checking if the raise amount is less than the minimum raise
* minimum raise defined in the game state. If it is, a RuleViolationException * defined in the game state. If it is, a RuleViolationException is thrown, indicating that the
* is thrown, indicating that the raise is too small. * raise is too small.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated, which should be an instance of * @param action The action to be validated, which should be an instance of RaiseAction.
* RaiseAction. * @throws RuleViolationException if the raise amount is less than the minimum raise.
* @throws RuleViolationException if the raise amount is less than the minimum
* raise.
*/ */
@Override @Override
public void validate(GameState state, Action action) { public void validate(GameState state, Action action) {
@@ -37,8 +34,7 @@ public class MinimumRaiseRule implements Rule {
int amount = raise.getAmount(); int amount = raise.getAmount();
if (amount < minRaise) { if (amount < minRaise) {
throw new RuleViolationException( throw new RuleViolationException("Raise too small. Min is " + minRaise);
"Raise too small. Min is " + minRaise);
} }
} }
} }
@@ -7,27 +7,25 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The OutOfTurnRule class implements the Rule interface and defines the * The OutOfTurnRule class implements the Rule interface and defines the validation logic for
* validation logic for out-of-turn actions in a poker game. It checks whether * out-of-turn actions in a poker game. It checks whether out-of-turn actions are allowed in the
* out-of-turn actions are allowed in the current game state and, if so, whether * current game state and, if so, whether the action type is permitted (e.g., only fold allowed out
* the action type is permitted (e.g., only fold allowed out of turn). If an * of turn). If an out-of-turn action is not allowed or if the action type is not permitted, a
* out-of-turn action is not allowed or if the action type is not permitted, * RuleViolationException is thrown.
* a RuleViolationException is thrown.
*/ */
public class OutOfTurnRule implements Rule { public class OutOfTurnRule implements Rule {
/** /**
* Validates the given action against the rules for out-of-turn actions in a * Validates the given action against the rules for out-of-turn actions in a poker game. If
* poker game. If out-of-turn actions are not allowed, the method returns * out-of-turn actions are not allowed, the method returns without throwing an exception. If
* without throwing an exception. If out-of-turn actions are allowed, it checks * out-of-turn actions are allowed, it checks whether the action type is permitted (e.g., only
* whether the action type is permitted (e.g., only fold allowed out of turn) * fold allowed out of turn) and throws a RuleViolationException if the action type is not
* and * permitted.
* throws a RuleViolationException if the action type is not permitted.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated. * @param action The action to be validated.
* @throws RuleViolationException if the action is not allowed out of turn or if * @throws RuleViolationException if the action is not allowed out of turn or if the action type
* the action type is not permitted. * is not permitted.
*/ */
@Override @Override
public void validate(GameState state, Action action) { public void validate(GameState state, Action action) {
@@ -7,28 +7,23 @@ import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationExc
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
/** /**
* The RaiseReopenRule class implements the Rule interface and defines the * The RaiseReopenRule class implements the Rule interface and defines the validation logic for
* validation logic for allowing raises to reopen betting in a poker game. It * allowing raises to reopen betting in a poker game. It checks if the action is a RaiseAction and
* checks if the action is a RaiseAction and if the betting is open for raising. * if the betting is open for raising. If betting is not open or if reopening betting is not
* If betting is not open or if reopening betting is not allowed, it throws a * allowed, it throws a RuleViolationException with an appropriate message.
* RuleViolationException with an appropriate message.
*/ */
public class RaiseReopenRule implements Rule { public class RaiseReopenRule implements Rule {
/** /**
* Validates the raise action by checking if the betting is open for raising and * Validates the raise action by checking if the betting is open for raising and if reopening
* if reopening betting is allowed. If betting is not open, a * betting is allowed. If betting is not open, a RuleViolationException is thrown with the
* RuleViolationException * message "Betting not open for raise". If reopening betting is not allowed, a
* is thrown with the message "Betting not open for raise". If reopening betting * RuleViolationException is thrown with the message "Raise not allowed anymore".
* is not allowed, a RuleViolationException is thrown with the message "Raise
* not
* allowed anymore".
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated, which should be an instance of * @param action The action to be validated, which should be an instance of RaiseAction.
* RaiseAction. * @throws RuleViolationException if betting is not open for raising or if reopening betting is
* @throws RuleViolationException if betting is not open for raising or if * not allowed.
* reopening betting is not allowed.
*/ */
@Override @Override
public void validate(GameState state, Action action) { public void validate(GameState state, Action action) {
@@ -12,21 +12,19 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* The CardsSpeakRule class implements the Rule interface and defines the logic * The CardsSpeakRule class implements the Rule interface and defines the logic for determining the
* for * winner of a poker hand based on the players' hole cards and the community cards. It evaluates
* determining the winner of a poker hand based on the players' hole cards and * each player's hand, compares their ranks, and awards the pot to the player with the best hand.
* the community cards. It evaluates each player's hand, compares their ranks,
* and awards the pot to the player with the best hand.
*/ */
public class CardsSpeakRule implements Rule { public class CardsSpeakRule implements Rule {
/** /**
* Validates the action for the showdown phase. In this implementation, the * Validates the action for the showdown phase. In this implementation, the CardsSpeakRule does
* CardsSpeakRule does not perform any validation, as it is responsible for * not perform any validation, as it is responsible for determining the winner based on the
* determining the winner based on the players' hands. The validation logic for * players' hands. The validation logic for player actions during the showdown phase should be
* player actions during the showdown phase should be handled by other rules. * handled by other rules.
* *
* @param state The current state of the game. * @param state The current state of the game.
* @param action The action to be validated. * @param action The action to be validated.
*/ */
@Override @Override
@@ -35,15 +33,13 @@ public class CardsSpeakRule implements Rule {
} }
/** /**
* Determines the winner of the poker hand by evaluating each player's hand * Determines the winner of the poker hand by evaluating each player's hand rank based on their
* rank based on their hole cards and the community cards. It compares the * hole cards and the community cards. It compares the hand ranks of all active players and
* hand ranks of all active players and returns the player with the best hand. * returns the player with the best hand.
* *
* @param state The current state of the game, which includes player * @param state The current state of the game, which includes player information, hole cards,
* information, * and community cards.
* hole cards, and community cards. * @return The player with the best hand, or null if there are no active players.
* @return The player with the best hand, or null if there are no active
* players.
*/ */
public Player determineWinner(GameState state) { public Player determineWinner(GameState state) {
@@ -76,13 +72,12 @@ public class CardsSpeakRule implements Rule {
} }
/** /**
* Awards the pot to the winner of the poker hand. It determines the winner(s) * Awards the pot to the winner of the poker hand. It determines the winner(s) using the
* using the determineWinner method, retrieves the total amount in the pot, * determineWinner method, retrieves the total amount in the pot, and adds it to the winner's
* and adds it to the winner's chips. Finally, it resets the pot for the next * chips. Finally, it resets the pot for the next hand.
* hand.
* *
* @param state The current state of the game, which includes player information * @param state The current state of the game, which includes player information and pot
* and pot details. * details.
*/ */
public void awardPot(GameState state) { public void awardPot(GameState state) {
@@ -4,11 +4,10 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** /**
* Represents the betting state of the current hand, including the pot size, * Represents the betting state of the current hand, including the pot size, the current highest
* the current highest bet, and the individual bets made by each player. * bet, and the individual bets made by each player. It keeps track of the total pot, the current
* It keeps track of the total pot, the current bet amount, and the bets made * bet amount, and the bets made by each player. This class is essential for managing the betting
* by each player. This class is essential for managing the betting rounds and * rounds and ensuring that all players' bets are accounted for during the game.
* ensuring that all players' bets are accounted for during the game.
*/ */
public class BettingState { public class BettingState {
@@ -1,11 +1,10 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
/** /**
* The GamePhase enumeration represents the different phases of a poker game. * The GamePhase enumeration represents the different phases of a poker game. Each phase corresponds
* Each phase corresponds to a specific stage in the game, such as waiting for * to a specific stage in the game, such as waiting for players, dealing cards, and determining the
* players, dealing cards, and determining the winner. This enumeration is * winner. This enumeration is essential for managing the flow of the game and ensuring that actions
* essential for managing the flow of the game and ensuring that actions are * are performed at the appropriate times.
* performed at the appropriate times.
*/ */
public enum GamePhase { public enum GamePhase {
WAITING, WAITING,
@@ -11,12 +11,11 @@ import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
* The GameState class encapsulates the entire state of a poker game at any * The GameState class encapsulates the entire state of a poker game at any given moment. It
* given * maintains information about the players, the pot, the current phase of the game, the dealer
* moment. It maintains information about the players, the pot, the current * position, and the cards in play. This class is central to managing the flow of the game and
* phase of the game, the dealer position, and the cards in play. This class is * ensuring that all actions and decisions are based on an accurate representation of the current
* central to managing the flow of the game and ensuring that all actions and * game state.
* decisions are based on an accurate representation of the current game state.
*/ */
public class GameState { public class GameState {
@@ -99,8 +98,7 @@ public class GameState {
} }
/** /**
* Returns the current state of the table, including player statuses and * Returns the current state of the table, including player statuses and positions.
* positions.
* *
* @return A TableState object representing the current state of the table. * @return A TableState object representing the current state of the table.
*/ */
@@ -109,8 +107,8 @@ public class GameState {
} }
/** /**
* Returns the current pot, which contains the total amount of chips bet by * Returns the current pot, which contains the total amount of chips bet by players in the
* players in the current hand. * current hand.
* *
* @return A Pot object representing the current pot. * @return A Pot object representing the current pot.
*/ */
@@ -140,10 +138,10 @@ public class GameState {
* Returns the hole cards for a specific player based on their ID. * Returns the hole cards for a specific player based on their ID.
* *
* @param playerId The ID of the player whose hole cards are being requested. * @param playerId The ID of the player whose hole cards are being requested.
* @return A list of Card objects representing the player's hole cards, or an * @return A list of Card objects representing the player's hole cards, or an empty list if the
* empty list if the player has no hole cards. * player has no hole cards.
*/ */
public List<Card> getHoleCards(PlayerId playerId) { public List<Card> getHoleCards(PlayerId playerId) {
return holeCards.getOrDefault(playerId, new ArrayList<>()); return holeCards.getOrDefault(playerId, new ArrayList<>());
} }
@@ -159,8 +157,8 @@ public class GameState {
/** /**
* Returns a map of player IDs to their current hole cards. * Returns a map of player IDs to their current hole cards.
* *
* @return A map where the key is the player ID and the value is a list of * @return A map where the key is the player ID and the value is a list of Card objects
* Card objects representing the player's hole cards. * representing the player's hole cards.
*/ */
public Map<PlayerId, List<Card>> getPlayerCards() { public Map<PlayerId, List<Card>> getPlayerCards() {
return holeCards; return holeCards;
@@ -216,19 +214,18 @@ public class GameState {
/** /**
* Sets the current deck of cards being used in the game. * Sets the current deck of cards being used in the game.
* *
* @param deck A Deck object representing the new deck of cards to be used in * @param deck A Deck object representing the new deck of cards to be used in the game.
* the game.
*/ */
public void setDeck(Deck deck) { public void setDeck(Deck deck) {
this.deck = deck; this.deck = deck;
} }
/** /**
* Adds a player to the game with the specified ID and initial chip count. This * Adds a player to the game with the specified ID and initial chip count. This method creates a
* method creates a new Player object, adds it to the list of players, and * new Player object, adds it to the list of players, and initializes the player's current bet
* initializes the player's current bet and bet commitment in the game state. * and bet commitment in the game state.
* *
* @param id The ID of the player to add. * @param id The ID of the player to add.
* @param chips The initial number of chips the player has. * @param chips The initial number of chips the player has.
*/ */
public void addPlayer(PlayerId id, int chips) { public void addPlayer(PlayerId id, int chips) {
@@ -248,37 +245,36 @@ public class GameState {
* Retrieves the current bet amount for a specific player based on their ID. * Retrieves the current bet amount for a specific player based on their ID.
* *
* @param playerId The ID of the player whose current bet is being requested. * @param playerId The ID of the player whose current bet is being requested.
* @return An integer representing the current bet amount for the specified * @return An integer representing the current bet amount for the specified player, or 0 if the
* player, or 0 if the player has not placed any bets. * player has not placed any bets.
*/ */
public int getCurrentBet(PlayerId playerId) { public int getCurrentBet(PlayerId playerId) {
return currentBets.getOrDefault(playerId, 0); return currentBets.getOrDefault(playerId, 0);
} }
/** /**
* Sets the current bet amount for a specific player based on their ID. This * Sets the current bet amount for a specific player based on their ID. This method updates the
* method updates the currentBets map with the new bet amount for the specified * currentBets map with the new bet amount for the specified player.
* player.
* *
* @param playerId The ID of the player whose current bet is being set. * @param playerId The ID of the player whose current bet is being set.
* @param amount The new bet amount to be set for the specified player. * @param amount The new bet amount to be set for the specified player.
*/ */
public void setCurrentBet(PlayerId playerId, int amount) { public void setCurrentBet(PlayerId playerId, int amount) {
currentBets.put(playerId, amount); currentBets.put(playerId, amount);
} }
/** /**
* Resets the current bets for all players by clearing the currentBets map. This * Resets the current bets for all players by clearing the currentBets map. This method is
* method is typically called at the start of a new hand to ensure that all * typically called at the start of a new hand to ensure that all players' bets are reset to
* players' bets are reset to zero. * zero.
*/ */
public void resetBets() { public void resetBets() {
currentBets.clear(); currentBets.clear();
} }
/** /**
* Adds a specified amount to the pot. This method updates the total amount in * Adds a specified amount to the pot. This method updates the total amount in the pot by adding
* the pot by adding the given amount to it. * the given amount to it.
* *
* @param amount The amount of chips to be added to the pot. * @param amount The amount of chips to be added to the pot.
*/ */
@@ -287,9 +283,9 @@ public class GameState {
} }
/** /**
* Returns whether out-of-turn actions are allowed in the game. Out-of-turn * Returns whether out-of-turn actions are allowed in the game. Out-of-turn actions refer to
* actions refer to players being able to act when it is not their turn, which * players being able to act when it is not their turn, which can be a feature in some poker
* can be a feature in some poker variants or game modes. * variants or game modes.
* *
* @return true if out-of-turn actions are allowed, false otherwise. * @return true if out-of-turn actions are allowed, false otherwise.
*/ */
@@ -298,12 +294,11 @@ public class GameState {
} }
/** /**
* Sets whether out-of-turn actions are allowed in the game. This method updates * Sets whether out-of-turn actions are allowed in the game. This method updates the
* the allowOutOfTurn flag, which determines if players can act when it is not * allowOutOfTurn flag, which determines if players can act when it is not their turn.
* their turn.
* *
* @param allowOutOfTurn A boolean value indicating whether out-of-turn actions * @param allowOutOfTurn A boolean value indicating whether out-of-turn actions should be
* should be allowed in the game. * allowed in the game.
*/ */
public void setAllowOutOfTurn(boolean allowOutOfTurn) { public void setAllowOutOfTurn(boolean allowOutOfTurn) {
this.allowOutOfTurn = allowOutOfTurn; this.allowOutOfTurn = allowOutOfTurn;
@@ -312,9 +307,9 @@ public class GameState {
// PLAYER HELPERS // PLAYER HELPERS
/** /**
* Retrieves a player from the game based on their ID. This method searches the * Retrieves a player from the game based on their ID. This method searches the list of players
* list of players for a player with the specified ID and returns it. If no * for a player with the specified ID and returns it. If no player with the given ID is found, a
* player with the given ID is found, a RuntimeException is thrown. * RuntimeException is thrown.
* *
* @param id The ID of the player to retrieve. * @param id The ID of the player to retrieve.
* @return The Player object corresponding to the specified ID. * @return The Player object corresponding to the specified ID.
@@ -333,29 +328,24 @@ public class GameState {
// BET COMMITMENTS // BET COMMITMENTS
/** /**
* Retrieves the current bet commitment for a specific player based on their ID. * Retrieves the current bet commitment for a specific player based on their ID. A bet
* A bet commitment represents the total amount a player has committed to the * commitment represents the total amount a player has committed to the pot in the current hand,
* pot in the current hand, including all bets, raises, and calls they have * including all bets, raises, and calls they have made.
* made.
* *
* @param playerId The ID of the player whose current bet commitment is being * @param playerId The ID of the player whose current bet commitment is being requested.
* requested. * @return An integer representing the current bet commitment for the specified player, or 0 if
* @return An integer representing the current bet commitment for the specified * the player has not made any bet commitments.
* player, or 0 if the player has not made any bet commitments.
*/ */
public int getCurrentBetCommitment(PlayerId playerId) { public int getCurrentBetCommitment(PlayerId playerId) {
return playerBetCommitments.getOrDefault(playerId, 0); return playerBetCommitments.getOrDefault(playerId, 0);
} }
/** /**
* Sets the current bet commitment for a specific player based on their ID. This * Sets the current bet commitment for a specific player based on their ID. This method updates
* method updates the playerBetCommitments map with the new bet commitment * the playerBetCommitments map with the new bet commitment amount for the specified player.
* amount for the specified player.
* *
* @param playerId The ID of the player whose current bet commitment is being * @param playerId The ID of the player whose current bet commitment is being set.
* set. * @param amount The new bet commitment amount to be set for the specified player.
* @param amount The new bet commitment amount to be set for the specified
* player.
*/ */
public void setCurrentBetCommitment(PlayerId playerId, int amount) { public void setCurrentBetCommitment(PlayerId playerId, int amount) {
playerBetCommitments.put(playerId, amount); playerBetCommitments.put(playerId, amount);
@@ -364,15 +354,13 @@ public class GameState {
// CARDS // CARDS
/** /**
* Gives hole cards to a specific player based on their ID. This method takes * Gives hole cards to a specific player based on their ID. This method takes two Card objects
* two Card objects representing the player's hole cards and adds them to the * representing the player's hole cards and adds them to the holeCards map under the player's
* holeCards map under the player's ID. * ID.
* *
* @param playerId The ID of the player to whom the hole cards are being given. * @param playerId The ID of the player to whom the hole cards are being given.
* @param c1 The first Card object representing one of the player's hole * @param c1 The first Card object representing one of the player's hole cards.
* cards. * @param c2 The second Card object representing the other hole card for the player.
* @param c2 The second Card object representing the other hole card for
* the player.
*/ */
public void giveHoleCards(PlayerId playerId, Card c1, Card c2) { public void giveHoleCards(PlayerId playerId, Card c1, Card c2) {
@@ -384,22 +372,19 @@ public class GameState {
} }
/** /**
* Adds a community card to the game state. This method takes a Card object * Adds a community card to the game state. This method takes a Card object representing a
* representing a community card and adds it to the list of community cards on * community card and adds it to the list of community cards on the table.
* the table.
* *
* @param card The Card object representing the community card to be added to * @param card The Card object representing the community card to be added to the game state.
* the
* game state.
*/ */
public void addCommunityCard(Card card) { public void addCommunityCard(Card card) {
communityCards.add(card); communityCards.add(card);
} }
/** /**
* Resets the community cards by clearing the list of community cards. This * Resets the community cards by clearing the list of community cards. This method is typically
* method is typically called at the start of a new hand to ensure that all * called at the start of a new hand to ensure that all community cards from the previous hand
* community cards from the previous hand are removed from the game state. * are removed from the game state.
*/ */
public void resetCommunityCards() { public void resetCommunityCards() {
communityCards.clear(); communityCards.clear();
@@ -408,10 +393,9 @@ public class GameState {
// TURN MANAGEMENT // TURN MANAGEMENT
/** /**
* Advances the turn to the next player in the players list. This method updates * Advances the turn to the next player in the players list. This method updates the
* the currentPlayerIndex by incrementing it and wrapping around to the start of * currentPlayerIndex by incrementing it and wrapping around to the start of the list if
* the list if necessary, ensuring that the turn order is maintained correctly * necessary, ensuring that the turn order is maintained correctly throughout the game.
* throughout the game.
*/ */
public void nextPlayer() { public void nextPlayer() {
int start = currentPlayerIndex; int start = currentPlayerIndex;
@@ -431,19 +415,17 @@ public class GameState {
// Dealer Rotation // Dealer Rotation
/** /**
* Rotates the dealer position to the next player in the players list. This * Rotates the dealer position to the next player in the players list. This method updates the
* method updates the dealerIndex by incrementing it and wrapping around to the * dealerIndex by incrementing it and wrapping around to the start of the list if necessary,
* start of the list if necessary, ensuring that the dealer position rotates * ensuring that the dealer position rotates correctly after each hand.
* correctly after each hand.
*/ */
public void rotateDealer() { public void rotateDealer() {
dealerIndex = (dealerIndex + 1) % playerOrder.size(); dealerIndex = (dealerIndex + 1) % playerOrder.size();
} }
/** /**
* Retrieves the current dealer based on the dealerIndex. This method returns * Retrieves the current dealer based on the dealerIndex. This method returns the Player object
* the Player object corresponding to the current dealer position in the players * corresponding to the current dealer position in the players list.
* list.
* *
* @return The Player object representing the current dealer. * @return The Player object representing the current dealer.
*/ */
@@ -455,12 +437,10 @@ public class GameState {
// HAND RESET // HAND RESET
/** /**
* Starts a new hand by resetting the game state for the next round of poker. * Starts a new hand by resetting the game state for the next round of poker. This method sets
* This * the handActive flag to true, resets the game phase to PREFLOP, clears the pot, resets all
* method sets the handActive flag to true, resets the game phase to PREFLOP, * player bets and bet commitments, clears the community cards, and initializes a new shuffled
* clears the pot, resets all player bets and bet commitments, clears the * deck of cards for the new hand.
* community cards, and initializes a new shuffled deck of cards for the new
* hand.
*/ */
public void startNewHand() { public void startNewHand() {
@@ -486,9 +466,9 @@ public class GameState {
} }
/** /**
* Folds a player in the current hand. This method adds the specified player's ID * Folds a player in the current hand. This method adds the specified player's ID to the set of
* to the set of folded players, indicating that the player has folded and is * folded players, indicating that the player has folded and is no longer active in the current
* no longer active in the current hand. * hand.
* *
* @param playerId The ID of the player who is folding. * @param playerId The ID of the player who is folding.
*/ */
@@ -497,9 +477,9 @@ public class GameState {
} }
/** /**
* Checks if a specific player has folded in the current hand. This method * Checks if a specific player has folded in the current hand. This method checks if the
* checks if the specified player's ID is present in the set of folded players, * specified player's ID is present in the set of folded players, indicating that the player has
* indicating that the player has folded. * folded.
* *
* @param playerId The ID of the player to check for folding status. * @param playerId The ID of the player to check for folding status.
* @return true if the player has folded, false otherwise. * @return true if the player has folded, false otherwise.
@@ -1,11 +1,10 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
/** /**
* The Pot class represents the total amount of chips that players have bet in a * The Pot class represents the total amount of chips that players have bet in a poker game. It
* poker game. It provides methods to add chips to the pot, retrieve the * provides methods to add chips to the pot, retrieve the current amount, and reset the pot for a
* current amount, and reset the pot for a new round. This class is essential * new round. This class is essential for managing the betting aspect of the game and ensuring that
* for managing the betting aspect of the game and ensuring that the pot is * the pot is accurately maintained throughout the game.
* accurately maintained throughout the game.
*/ */
public class Pot { public class Pot {
@@ -36,10 +35,7 @@ public class Pot {
// return total; // return total;
// } // }
/** /** Resets the pot to zero, typically used at the end of a round or when starting a new game. */
* Resets the pot to zero, typically used at the end of a round or when
* starting a new game.
*/
public void reset() { public void reset() {
amount = 0; amount = 0;
} }
@@ -1,12 +1,11 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
/** /**
* The TableState class represents the current state of the poker table during a * The TableState class represents the current state of the poker table during a game. It keeps
* game. It keeps track of the current bet, the last raise size, the minimum * track of the current bet, the last raise size, the minimum raise amount, and the big blind.
* raise amount, and the big blind. Additionally, it manages the betting status, * Additionally, it manages the betting status, including whether betting is currently open and if
* including whether betting is currently open and if it can be reopened after * it can be reopened after being closed. The class also tracks the last aggressor's ID to determine
* being closed. The class also tracks the last aggressor's ID to determine who * who made the most recent aggressive action (like a raise) in the betting round.
* made the most recent aggressive action (like a raise) in the betting round.
*/ */
public class TableState { public class TableState {
@@ -21,9 +20,9 @@ public class TableState {
private String lastAggressorId; private String lastAggressorId;
/** /**
* Retrieves the current bet amount for the table. This value represents the * Retrieves the current bet amount for the table. This value represents the highest bet that
* highest bet that has been placed in the current betting round and is used to * has been placed in the current betting round and is used to determine how much players need
* determine how much players need to call or raise to stay in the hand. * to call or raise to stay in the hand.
* *
* @return The current bet amount for the table. * @return The current bet amount for the table.
*/ */
@@ -32,9 +31,8 @@ public class TableState {
} }
/** /**
* Sets the current bet amount for the table. This method is typically called * Sets the current bet amount for the table. This method is typically called when a player
* when a player places a bet or raises, updating the current bet to reflect * places a bet or raises, updating the current bet to reflect the new amount.
* the new amount.
* *
* @param currentBet The new current bet amount to set for the table. * @param currentBet The new current bet amount to set for the table.
*/ */
@@ -43,8 +41,8 @@ public class TableState {
} }
/** /**
* Returns the big blind amount. The big blind serves as a baseline * Returns the big blind amount. The big blind serves as a baseline for betting and is often
* for betting and is often used to determine the minimum bet or raise. * used to determine the minimum bet or raise.
* *
* @return The big blind amount. * @return The big blind amount.
*/ */
@@ -53,9 +51,8 @@ public class TableState {
} }
/** /**
* Sets the big blind amount for the game. The big blind is a forced bet that * Sets the big blind amount for the game. The big blind is a forced bet that players must post
* players must post before the cards are dealt, and it serves as a baseline for * before the cards are dealt, and it serves as a baseline for betting in the game.
* betting in the game.
* *
* @param bigBlind The amount to set as the big blind for the game. * @param bigBlind The amount to set as the big blind for the game.
*/ */
@@ -64,9 +61,9 @@ public class TableState {
} }
/** /**
* Retrieves the minimum raise amount for the current betting round. The * Retrieves the minimum raise amount for the current betting round. The minimum raise is
* minimum raise is typically determined by the size of the last raise or the * typically determined by the size of the last raise or the big blind, ensuring that players
* big blind, ensuring that players must raise by at least a certain amount. * must raise by at least a certain amount.
* *
* @return The minimum raise amount for the current betting round. * @return The minimum raise amount for the current betting round.
*/ */
@@ -75,21 +72,19 @@ public class TableState {
} }
/** /**
* Sets the minimum raise amount for the current betting round. This method is * Sets the minimum raise amount for the current betting round. This method is typically called
* typically called after a raise is made, updating the minimum raise to ensure * after a raise is made, updating the minimum raise to ensure that subsequent raises meet the
* that subsequent raises meet the required amount. * required amount.
* *
* @param minRaise The new minimum raise amount to set for the current betting * @param minRaise The new minimum raise amount to set for the current betting round.
* round.
*/ */
public void setMinRaise(int minRaise) { public void setMinRaise(int minRaise) {
this.minRaise = minRaise; this.minRaise = minRaise;
} }
/** /**
* Checks if betting is currently open at the table. This status indicates * Checks if betting is currently open at the table. This status indicates whether players are
* whether players are allowed to place bets or raises during the current * allowed to place bets or raises during the current betting round.
* betting round.
* *
* @return true if betting is open, false otherwise. * @return true if betting is open, false otherwise.
*/ */
@@ -98,21 +93,20 @@ public class TableState {
} }
/** /**
* Sets the betting status for the table. This method can be used to open or * Sets the betting status for the table. This method can be used to open or close betting
* close betting during a betting round, controlling whether players can place * during a betting round, controlling whether players can place bets or raises.
* bets or raises.
* *
* @param bettingOpen The new betting status to set for the table (true for * @param bettingOpen The new betting status to set for the table (true for open, false for
* open, false for closed). * closed).
*/ */
public void setBettingOpen(boolean bettingOpen) { public void setBettingOpen(boolean bettingOpen) {
this.bettingOpen = bettingOpen; this.bettingOpen = bettingOpen;
} }
/** /**
* Checks if betting can be reopened after being closed. This status allows for * Checks if betting can be reopened after being closed. This status allows for scenarios where
* scenarios where betting may be temporarily closed (e.g., after a raise) but * betting may be temporarily closed (e.g., after a raise) but can be reopened to allow other
* can be reopened to allow other players to respond. * players to respond.
* *
* @return true if betting can be reopened, false otherwise. * @return true if betting can be reopened, false otherwise.
*/ */
@@ -121,22 +115,21 @@ public class TableState {
} }
/** /**
* Sets whether betting can be reopened after being closed. This method is * Sets whether betting can be reopened after being closed. This method is typically called
* typically called after a raise is made, allowing for the possibility of * after a raise is made, allowing for the possibility of reopening betting to let other players
* reopening betting to let other players respond to the raise. * respond to the raise.
* *
* @param canReopenBetting The new status indicating whether betting can be * @param canReopenBetting The new status indicating whether betting can be reopened (true or
* reopened (true or false). * false).
*/ */
public void setCanReopenBetting(boolean canReopenBetting) { public void setCanReopenBetting(boolean canReopenBetting) {
this.canReopenBetting = canReopenBetting; this.canReopenBetting = canReopenBetting;
} }
/** /**
* Retrieves the ID of the last aggressor in the current betting round. The * Retrieves the ID of the last aggressor in the current betting round. The last aggressor is
* last aggressor is the player who made the most recent aggressive action (like * the player who made the most recent aggressive action (like a raise) and is important for
* a raise) and is important for determining the flow of betting and who may * determining the flow of betting and who may have the opportunity to respond.
* have the opportunity to respond.
* *
* @return The ID of the last aggressor in the current betting round. * @return The ID of the last aggressor in the current betting round.
*/ */
@@ -145,12 +138,11 @@ public class TableState {
} }
/** /**
* Sets the ID of the last aggressor in the current betting round. This method * Sets the ID of the last aggressor in the current betting round. This method is typically
* is typically called after a player makes an aggressive action (like a raise), * called after a player makes an aggressive action (like a raise), updating the last aggressor
* updating the last aggressor ID to reflect the most recent aggressive action. * ID to reflect the most recent aggressive action.
* *
* @param lastAggressorId The new ID of the last aggressor to set for the * @param lastAggressorId The new ID of the last aggressor to set for the current betting round.
* current betting round.
*/ */
public void setLastAggressorId(String lastAggressorId) { public void setLastAggressorId(String lastAggressorId) {
this.lastAggressorId = lastAggressorId; this.lastAggressorId = lastAggressorId;
@@ -1,57 +1,58 @@
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game; package ch.unibas.dmi.dbis.cs108.casono.server.domain.game;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Deck; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Deck;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Rank; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Rank;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.GameEngine; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.GameEngine;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandEvaluator;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandRank;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.RoundManager; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.RoundManager;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.TurnManager; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine.TurnManager;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandEvaluator;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator.HandRank;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GamePhase; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GamePhase;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card; import java.util.ArrayList;
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player; import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import java.util.*;
import static org.junit.jupiter.api.Assertions.*;
import java.util.*;
import java.util.logging.Logger;
/** /**
* The GameControllerTest class contains unit tests for the GameController * The GameControllerTest class contains unit tests for the GameController class, which manages the
* class, * flow of a poker game. These tests simulate various game scenarios, including player actions, card
* which manages the flow of a poker game. These tests simulate various game * dealing, and hand evaluation, to ensure that the GameController behaves as expected under
* scenarios, including player actions, card dealing, and hand evaluation, to * different conditions.
* ensure that the GameController behaves as expected under different
* conditions.
*/ */
public class GameControllerTest { public class GameControllerTest {
private static final Logger logger = Logger.getLogger(GameControllerTest.class.getName()); private static final Logger LOGGER = Logger.getLogger(GameControllerTest.class.getName());
/** /**
* This test simulates a specific game scenario where Julian is expected to * This test simulates a specific game scenario where Julian is expected to win with a Two Pair
* win with a Two Pair hand. It sets up a fixed deck to ensure that the * hand. It sets up a fixed deck to ensure that the desired cards are dealt to the players and
* desired cards are dealt to the players and verifies that the hand * verifies that the hand evaluation correctly identifies Julian as the winner.
* evaluation correctly identifies Julian as the winner.
*/ */
@Test @Test
public void testFullPokerGameSimulation1() { public void testFullPokerGameSimulation1() {
GameState state = new GameState(); GameState state = new GameState();
GameEngine engine = new GameEngine( GameEngine engine =
state, new GameEngine(
new RuleEngine(new ArrayList<>()), state,
new RoundManager(), new RuleEngine(new ArrayList<>()),
new TurnManager()); new RoundManager(),
new TurnManager());
GameController game = new GameController(engine); GameController game = new GameController(engine);
@@ -116,24 +117,25 @@ public class GameControllerTest {
assertEquals(4, game.getState().getPlayers().size()); assertEquals(4, game.getState().getPlayers().size());
logger.info("Test 1 was successfully completed"); LOGGER.info("Test 1 was successfully completed");
} }
/** /**
* This test simulates a full poker game, including player actions, card * This test simulates a full poker game, including player actions, card dealing, and hand
* dealing, and hand evaluation. It verifies that the game state is updated * evaluation. It verifies that the game state is updated correctly throughout the game and that
* correctly throughout the game and that the expected outcomes are achieved. * the expected outcomes are achieved.
*/ */
@Test @Test
public void testFullPokerGameSimulation2() { public void testFullPokerGameSimulation2() {
GameState state = new GameState(); GameState state = new GameState();
GameEngine engine = new GameEngine( GameEngine engine =
state, new GameEngine(
new RuleEngine(new ArrayList<>()), state,
new RoundManager(), new RuleEngine(new ArrayList<>()),
new TurnManager()); new RoundManager(),
new TurnManager());
GameController game = new GameController(engine); GameController game = new GameController(engine);
@@ -142,24 +144,15 @@ public class GameControllerTest {
game.addPlayer(PlayerId.of("Jona"), 20000); game.addPlayer(PlayerId.of("Jona"), 20000);
game.addPlayer(PlayerId.of("Lars"), 20000); game.addPlayer(PlayerId.of("Lars"), 20000);
assertEquals( assertEquals(4, game.getState().getPlayers().size(), "There should be exactly 4 players");
4,
game.getState().getPlayers().size(),
"There should be exactly 4 players");
game.startGame(); game.startGame();
assertEquals( assertEquals(GamePhase.PREFLOP, game.getState().getPhase(), "The hand must start preflop");
GamePhase.PREFLOP,
game.getState().getPhase(),
"The hand must start preflop");
Map<PlayerId, List<Card>> playerCards = game.getPlayerCards(); Map<PlayerId, List<Card>> playerCards = game.getPlayerCards();
assertEquals( assertEquals(4, playerCards.size(), "All players must receive cards");
4,
playerCards.size(),
"All players must receive cards");
for (Map.Entry<PlayerId, List<Card>> entry : playerCards.entrySet()) { for (Map.Entry<PlayerId, List<Card>> entry : playerCards.entrySet()) {
@@ -177,9 +170,7 @@ public class GameControllerTest {
String key = c.getRank() + "-" + c.getSuit(); String key = c.getRank() + "-" + c.getSuit();
assertFalse( assertFalse(seenCards.contains(key), "Duplicate card found: " + key);
seenCards.contains(key),
"Duplicate card found: " + key);
seenCards.add(key); seenCards.add(key);
} }
@@ -196,91 +187,68 @@ public class GameControllerTest {
int potAfter = game.getState().getPot().getAmount(); int potAfter = game.getState().getPot().getAmount();
assertTrue( assertTrue(potAfter >= potBefore, "The pot must be larger after actions");
potAfter >= potBefore,
"The pot must be larger after actions");
game.dealFlop(); game.dealFlop();
assertEquals( assertEquals(3, game.getCommunityCards().size(), "The flop must have 3 cards");
3,
game.getCommunityCards().size(),
"The flop must have 3 cards");
game.dealTurn(); game.dealTurn();
assertEquals( assertEquals(4, game.getCommunityCards().size(), "A turn must result in 4 cards");
4,
game.getCommunityCards().size(),
"A turn must result in 4 cards");
game.dealRiver(); game.dealRiver();
assertEquals( assertEquals(5, game.getCommunityCards().size(), "The river must consist of 5 cards");
5,
game.getCommunityCards().size(),
"The river must consist of 5 cards");
for (Card c : game.getCommunityCards()) { for (Card c : game.getCommunityCards()) {
String key = c.getRank() + "-" + c.getSuit(); String key = c.getRank() + "-" + c.getSuit();
assertFalse( assertFalse(seenCards.contains(key), "Duplicate board card detected: " + key);
seenCards.contains(key),
"Duplicate board card detected: " + key);
seenCards.add(key); seenCards.add(key);
} }
assertTrue( assertTrue(seenCards.size() <= 52, "There can be a maximum of 52 cards");
seenCards.size() <= 52,
"There can be a maximum of 52 cards");
assertTrue( assertTrue(game.getState().getPot().getAmount() > 0, "The pot must contain chips");
game.getState().getPot().getAmount() > 0,
"The pot must contain chips");
for (Player p : game.getState().getPlayers()) { for (Player p : game.getState().getPlayers()) {
assertTrue( assertTrue(p.getChips() >= 0, "A player may not have any negative chips: " + p.getId());
p.getChips() >= 0,
"A player may not have any negative chips: " + p.getId());
} }
assertEquals( assertEquals(
4, 4, game.getState().getPlayers().size(), "The number of players must not change");
game.getState().getPlayers().size(),
"The number of players must not change");
for (Player p : game.getState().getPlayers()) { for (Player p : game.getState().getPlayers()) {
logger.info( LOGGER.info(p.getId() + " | Chips: " + p.getChips());
p.getId() +
" | Chips: " + p.getChips());
} }
logger.info("Pot: " + game.getState().getPot().getAmount()); LOGGER.info("Pot: " + game.getState().getPot().getAmount());
logger.info("Board: " + game.getCommunityCards()); LOGGER.info("Board: " + game.getCommunityCards());
logger.info("Test 2 was successfully completed"); LOGGER.info("Test 2 was successfully completed");
} }
/** /**
* This test simulates a specific game scenario where Julian is expected to * This test simulates a specific game scenario where Julian is expected to win with a Two Pair
* win with a Two Pair hand. It sets up a fixed deck to ensure that the * hand. It sets up a fixed deck to ensure that the desired cards are dealt to the players and
* desired cards are dealt to the players and verifies that the hand * verifies that the hand evaluation correctly identifies Julian as the winner.
* evaluation correctly identifies Julian as the winner.
*/ */
@Test @Test
public void testFullPokerGameSimulation3() { public void testFullPokerGameSimulation3() {
GameState state = new GameState(); GameState state = new GameState();
GameEngine engine = new GameEngine( GameEngine engine =
state, new GameEngine(
new RuleEngine(new ArrayList<>()), state,
new RoundManager(), new RuleEngine(new ArrayList<>()),
new TurnManager()); new RoundManager(),
new TurnManager());
GameController game = new GameController(engine); GameController game = new GameController(engine);
@@ -350,25 +318,26 @@ public class GameControllerTest {
assertTrue(julianRank.compareTo(larsRank) > 0); assertTrue(julianRank.compareTo(larsRank) > 0);
logger.info("Correct scenario: Julian wins with two pair."); LOGGER.info("Correct scenario: Julian wins with two pair.");
logger.info("Test 3 was successfully completed"); LOGGER.info("Test 3 was successfully completed");
} }
/** /**
* This test simulates a full poker game, including player actions, card * This test simulates a full poker game, including player actions, card dealing, and hand
* dealing, and hand evaluation. It verifies that the game state is updated * evaluation. It verifies that the game state is updated correctly throughout the game and that
* correctly throughout the game and that the expected outcomes are achieved. * the expected outcomes are achieved.
*/ */
@Test @Test
public void testFullPokerGameSimulation4() { public void testFullPokerGameSimulation4() {
GameState state = new GameState(); GameState state = new GameState();
GameEngine engine = new GameEngine( GameEngine engine =
state, new GameEngine(
new RuleEngine(new ArrayList<>()), state,
new RoundManager(), new RuleEngine(new ArrayList<>()),
new TurnManager()); new RoundManager(),
new TurnManager());
GameController game = new GameController(engine); GameController game = new GameController(engine);
@@ -431,35 +400,34 @@ public class GameControllerTest {
assertTrue(game.getState().getPot().getAmount() > 0, "Pot must not be zero"); assertTrue(game.getState().getPot().getAmount() > 0, "Pot must not be zero");
assertEquals(4, game.getState().getPlayers().size(), assertEquals(4, game.getState().getPlayers().size(), "Player count must remain stable");
"Player count must remain stable");
long activePlayers = game.getState().getPlayers().stream() long activePlayers =
.filter(p -> p.getChips() > 0) game.getState().getPlayers().stream().filter(p -> p.getChips() > 0).count();
.count();
assertTrue(activePlayers >= 1, "At least one player must still have chips"); assertTrue(activePlayers >= 1, "At least one player must still have chips");
assertNotNull(game.getState().getPhase()); assertNotNull(game.getState().getPhase());
logger.info("Test 4 was successfully completed"); LOGGER.info("Test 4 was successfully completed");
} }
/** /**
* This test simulates a full poker game, including player actions, card * This test simulates a full poker game, including player actions, card dealing, and hand
* dealing, and hand evaluation. It verifies that the game state is updated * evaluation. It verifies that the game state is updated correctly throughout the game and that
* correctly throughout the game and that the expected outcomes are achieved. * the expected outcomes are achieved.
*/ */
@Test @Test
public void testFullPokerGameSimulation5() { public void testFullPokerGameSimulation5() {
GameState state = new GameState(); GameState state = new GameState();
GameEngine engine = new GameEngine( GameEngine engine =
state, new GameEngine(
new RuleEngine(new ArrayList<>()), state,
new RoundManager(), new RuleEngine(new ArrayList<>()),
new TurnManager()); new RoundManager(),
new TurnManager());
GameController game = new GameController(engine); GameController game = new GameController(engine);
@@ -476,8 +444,7 @@ public class GameControllerTest {
game.playerCall(PlayerId.of("ShortStack")); game.playerCall(PlayerId.of("ShortStack"));
for (Player p : game.getState().getPlayers()) { for (Player p : game.getState().getPlayers()) {
assertTrue(p.getChips() >= 0, assertTrue(p.getChips() >= 0, "Negative chips detected for " + p.getId());
"Negative chips detected for " + p.getId());
} }
assertTrue(game.getState().getPot().getAmount() > 0); assertTrue(game.getState().getPot().getAmount() > 0);
@@ -490,9 +457,8 @@ public class GameControllerTest {
game.playerFold(PlayerId.of("Caller")); game.playerFold(PlayerId.of("Caller"));
game.playerFold(PlayerId.of("MidStack")); game.playerFold(PlayerId.of("MidStack"));
long activePlayers = game.getState().getPlayers().stream() long activePlayers =
.filter(p -> p.getChips() > 0) game.getState().getPlayers().stream().filter(p -> p.getChips() > 0).count();
.count();
assertTrue(activePlayers >= 1, "At least one player must remain"); assertTrue(activePlayers >= 1, "At least one player must remain");
@@ -529,12 +495,10 @@ public class GameControllerTest {
assertNotNull(winner, "Winner should not be null"); assertNotNull(winner, "Winner should not be null");
assertTrue( assertTrue(game.getPlayerCards().containsKey(winner), "Winner must be a valid player");
game.getPlayerCards().containsKey(winner),
"Winner must be a valid player");
logger.info("Winner determined: " + winner); LOGGER.info("Winner determined: " + winner);
logger.info("Test 5 was successfully completed"); LOGGER.info("Test 5 was successfully completed");
} }
} }