From 769105b87d1d0cbddfe46ae14878373299a11d34 Mon Sep 17 00:00:00 2001 From: Julian Kropff Date: Sat, 4 Apr 2026 23:38:19 +0200 Subject: [PATCH] Add: rule system for game engine --- .../casono/server/domain/game/rules/Rule.java | 15 +++ .../server/domain/game/rules/RuleEngine.java | 42 ++++++++ .../game/rules/RuleViolationException.java | 20 ++++ .../rules/betting/AcceptedActionRule.java | 38 +++++++ .../game/rules/betting/ActionOrderRule.java | 42 ++++++++ .../domain/game/rules/betting/AllInRule.java | 40 +++++++ .../rules/betting/BindingDeclarationRule.java | 42 ++++++++ .../game/rules/betting/HandActiveRule.java | 32 ++++++ .../game/rules/betting/MinimumBetRule.java | 40 +++++++ .../game/rules/betting/MinimumRaiseRule.java | 44 ++++++++ .../game/rules/betting/OutOfTurnRule.java | 43 ++++++++ .../game/rules/betting/RaiseReopenRule.java | 48 +++++++++ .../game/rules/showdown/CardsSpeakRule.java | 101 ++++++++++++++++++ 13 files changed, 547 insertions(+) create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/Rule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleEngine.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleViolationException.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AcceptedActionRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/ActionOrderRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AllInRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/BindingDeclarationRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/HandActiveRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumBetRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumRaiseRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/OutOfTurnRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/RaiseReopenRule.java create mode 100644 src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/Rule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/Rule.java new file mode 100644 index 0000000..b067b21 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/Rule.java @@ -0,0 +1,15 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +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. + * Each rule must provide a validate method that checks if a given action is + * valid based on the current game state. If the action violates the rule, a + * RuleViolationException should be thrown to indicate the specific rule that + * was violated. + */ +public interface Rule { + void validate(GameState state, Action action); +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleEngine.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleEngine.java new file mode 100644 index 0000000..7e06097 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleEngine.java @@ -0,0 +1,42 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules; + +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 java.util.List; + +/** + * The RuleEngine class is responsible for managing and validating a list of + * rules in the poker game. It provides a method to validate a given action + * against all the rules, ensuring that the action complies with the game's + * regulations. If any rule is violated during validation, a + * RuleViolationException + * will be thrown, indicating the specific rule that was not followed. + */ +public class RuleEngine { + + private final List rules; + + /** + * Constructs a RuleEngine with the specified list of rules. + * + * @param rules The list of rules to be managed by the RuleEngine. + */ + public RuleEngine(List rules) { + this.rules = rules; + } + + /** + * Validates the given action against all the rules in the RuleEngine. If any + * rule is violated, a RuleViolationException will be thrown, indicating the + * specific rule that was not followed. + * + * @param state The current state of the game. + * @param action The action to be validated against the rules. + * @throws RuleViolationException if any rule is violated during validation. + */ + public void validate(GameState state, Action action) { + for (Rule rule : rules) { + rule.validate(state, action); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleViolationException.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleViolationException.java new file mode 100644 index 0000000..2024091 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/RuleViolationException.java @@ -0,0 +1,20 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules; + +/** + * RuleViolationException is a custom exception that is thrown when a player + * action violates a specific rule in the poker game. This exception provides + * a message detailing the nature of the rule violation, allowing for better + * error handling and user feedback when invalid actions are attempted. + */ +public class RuleViolationException extends RuntimeException { + + /** + * Constructs a new RuleViolationException with the specified detail message. + * + * @param message The detail message explaining the reason for the rule + * violation. + */ + public RuleViolationException(String message) { + super(message); + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AcceptedActionRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AcceptedActionRule.java new file mode 100644 index 0000000..54c1380 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AcceptedActionRule.java @@ -0,0 +1,38 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.ActionType; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The AcceptedActionRule class implements the Rule interface and defines the + * validation logic for accepted player actions in a poker game. It checks + * whether the action type is one of the allowed types (FOLD, CALL, RAISE, + * CHECK, BET, ALL_IN) and throws a RuleViolationException if the action is not + * allowed. + */ +public class AcceptedActionRule implements Rule { + + /** + * Validates the given action against the accepted action types for a poker + * game. If the action type is not one of the allowed types, a + * RuleViolationException is thrown. + * + * @param state The current state of the game. + * @param action The action to be validated. + * @throws RuleViolationException if the action type is not allowed. + */ + @Override + public void validate(GameState state, Action action) { + + ActionType type = action.getType(); + + switch (type) { + case FOLD, CALL, RAISE, CHECK, BET, ALL_IN -> { + } + default -> throw new RuleViolationException("Action not allowed"); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/ActionOrderRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/ActionOrderRule.java new file mode 100644 index 0000000..528fb04 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/ActionOrderRule.java @@ -0,0 +1,42 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +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.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; +import java.util.ArrayList; +import java.util.List; + +/** + * The ActionOrderRule class implements the Rule interface and defines the + * validation logic for ensuring that players take their turns in the correct + * order during a poker game. It checks whether the player performing the action + * is the current player whose turn it is, and throws a RuleViolationException + * if the action is attempted out of turn. + */ +public class ActionOrderRule implements Rule { + + /** + * Validates that the player performing the action is the current player + * whose turn it is in the game. If the action is attempted by a player who + * is not the current player, a RuleViolationException is thrown. + * + * @param state The current state of the game. + * @param action The action to be validated. + * @throws RuleViolationException if the action is attempted out of turn. + */ + @Override + public void validate(GameState state, Action action) { + + PlayerId actingPlayerId = action.getPlayerId(); + + List playerList = new ArrayList<>(state.getPlayers()); + Player current = playerList.get(state.getCurrentPlayerIndex()); + + if (!current.getId().equals(actingPlayerId)) { + throw new RuleViolationException("Not your turn"); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AllInRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AllInRule.java new file mode 100644 index 0000000..efa95cd --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/AllInRule.java @@ -0,0 +1,40 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.AllInAction; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.Player; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The AllInRule class implements the Rule interface and defines the validation + * logic for the "all-in" action in a poker game. It checks whether the player + * attempting to go all-in has any chips left, and throws a + * RuleViolationException + * if the player has no chips to bet. + */ +public class AllInRule implements Rule { + + /** + * Validates the "all-in" action by checking if the player has any chips left. + * If the player has no chips, a RuleViolationException is thrown. + * + * @param state The current state of the game. + * @param action The action to be validated, which should be an instance of + * AllInAction. + * @throws RuleViolationException if the player has no chips to go all-in. + */ + @Override + public void validate(GameState state, Action action) { + + if (action instanceof AllInAction allIn) { + + Player player = state.getPlayer(allIn.getPlayerId()); + + if (player.getChips() <= 0) { + throw new RuleViolationException("No chips for all-in"); + } + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/BindingDeclarationRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/BindingDeclarationRule.java new file mode 100644 index 0000000..cd8a38d --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/BindingDeclarationRule.java @@ -0,0 +1,42 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.RaiseAction; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The BindingDeclarationRule class implements the Rule interface and defines + * the + * validation logic for binding declarations in a poker game. It checks whether + * a raise action is an under-raise or string bet by comparing the raise amount + * 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 { + + /** + * Validates the raise action by checking if the raise amount is less than the + * current bet commitment of the player. If it is, a RuleViolationException is + * thrown, indicating an illegal under-raise or string bet. + * + * @param state The current state of the game. + * @param action The action to be validated, which should be an instance of + * RaiseAction. + * @throws RuleViolationException if the raise amount is less than the current + * bet commitment. + */ + @Override + public void validate(GameState state, Action action) { + + if (action instanceof RaiseAction raise) { + + int committed = state.getCurrentBetCommitment(action.getPlayerId()); + + if (raise.getAmount() < committed) { + throw new RuleViolationException("Illegal under-raise / string bet"); + } + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/HandActiveRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/HandActiveRule.java new file mode 100644 index 0000000..5ae71ef --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/HandActiveRule.java @@ -0,0 +1,32 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The HandActiveRule class implements the Rule interface and defines the + * validation logic for checking if there is an active hand in a poker game. + * It ensures that players can only perform actions when there is an active + * hand, + * and throws a RuleViolationException if there is no active hand. + */ +public class HandActiveRule implements Rule { + + /** + * Validates that there is an active hand in the game. If there is no active + * hand, a RuleViolationException is thrown, indicating that players cannot + * perform actions without an active hand. + * + * @param state The current state of the game. + * @param action The action to be validated. + * @throws RuleViolationException if there is no active hand in the game. + */ + @Override + public void validate(GameState state, Action action) { + if (!state.isHandActive()) { + throw new RuleViolationException("No active hand"); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumBetRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumBetRule.java new file mode 100644 index 0000000..bf6b694 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumBetRule.java @@ -0,0 +1,40 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.BetAction; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The MinimumBetRule class implements the Rule interface and defines the + * validation logic for ensuring that a player's bet meets the minimum bet + * requirement in a poker game. It checks if the bet amount is less than the + * minimum bet (usually determined by the big blind) and throws a + * RuleViolationException if the bet is below the minimum. + */ +public class MinimumBetRule implements Rule { + + /** + * Validates the bet action by checking if the bet amount is less than the + * minimum bet. If it is, a RuleViolationException is thrown, indicating that + * the bet is below the minimum. + * + * @param state The current state of the game. + * @param action The action to be validated, which should be an instance of + * BetAction. + * @throws RuleViolationException if the bet amount is below the minimum bet. + */ + @Override + public void validate(GameState state, Action action) { + + if (action instanceof BetAction bet) { + + int minBet = state.getTableState().getBigBlind(); + + if (bet.getAmount() < minBet) { + throw new RuleViolationException("Bet below minimum"); + } + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumRaiseRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumRaiseRule.java new file mode 100644 index 0000000..ab8eb41 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/MinimumRaiseRule.java @@ -0,0 +1,44 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.RaiseAction; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The MinimumRaiseRule class implements the Rule interface and defines the + * validation logic for ensuring that a raise action in a poker game meets the + * minimum raise requirement. It checks if the amount of the raise is less than + * the minimum raise defined in the game state, and if so, it throws a + * RuleViolationException indicating that the raise is too small. + */ +public class MinimumRaiseRule implements Rule { + + /** + * Validates the raise action by checking if the raise amount is less than the + * minimum raise defined in the game state. If it is, a RuleViolationException + * is thrown, indicating that the raise is too small. + * + * @param state The current state of the game. + * @param action The action to be validated, which should be an instance of + * RaiseAction. + * @throws RuleViolationException if the raise amount is less than the minimum + * raise. + */ + @Override + public void validate(GameState state, Action action) { + + if (!(action instanceof RaiseAction raise)) { + return; + } + + int minRaise = state.getTableState().getMinRaise(); + int amount = raise.getAmount(); + + if (amount < minRaise) { + throw new RuleViolationException( + "Raise too small. Min is " + minRaise); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/OutOfTurnRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/OutOfTurnRule.java new file mode 100644 index 0000000..880953f --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/OutOfTurnRule.java @@ -0,0 +1,43 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.ActionType; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The OutOfTurnRule class implements the Rule interface and defines the + * validation logic for out-of-turn actions in a poker game. It checks whether + * out-of-turn actions are allowed in the current game state and, if so, whether + * the action type is permitted (e.g., only fold allowed out of turn). If an + * out-of-turn action is not allowed or if the action type is not permitted, + * a RuleViolationException is thrown. + */ +public class OutOfTurnRule implements Rule { + + /** + * Validates the given action against the rules for out-of-turn actions in a + * poker game. If out-of-turn actions are not allowed, the method returns + * without throwing an exception. If out-of-turn actions are allowed, it checks + * whether the action type is permitted (e.g., only fold allowed out of turn) + * and + * throws a RuleViolationException if the action type is not permitted. + * + * @param state The current state of the game. + * @param action The action to be validated. + * @throws RuleViolationException if the action is not allowed out of turn or if + * the action type is not permitted. + */ + @Override + public void validate(GameState state, Action action) { + + if (!state.isAllowOutOfTurn()) { + return; // strikt Mode + } + + if (action.getType() != ActionType.FOLD) { + throw new RuleViolationException("Only fold allowed out of turn"); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/RaiseReopenRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/RaiseReopenRule.java new file mode 100644 index 0000000..6a474b2 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/betting/RaiseReopenRule.java @@ -0,0 +1,48 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.betting; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.RaiseAction; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleViolationException; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; + +/** + * The RaiseReopenRule class implements the Rule interface and defines the + * validation logic for allowing raises to reopen betting in a poker game. It + * checks if the action is a RaiseAction and if the betting is open for raising. + * If betting is not open or if reopening betting is not allowed, it throws a + * RuleViolationException with an appropriate message. + */ +public class RaiseReopenRule implements Rule { + + /** + * Validates the raise action by checking if the betting is open for raising and + * if reopening betting is allowed. If betting is not open, a + * RuleViolationException + * is thrown with the message "Betting not open for raise". If reopening betting + * is not allowed, a RuleViolationException is thrown with the message "Raise + * not + * allowed anymore". + * + * @param state The current state of the game. + * @param action The action to be validated, which should be an instance of + * RaiseAction. + * @throws RuleViolationException if betting is not open for raising or if + * reopening betting is not allowed. + */ + @Override + public void validate(GameState state, Action action) { + + if (!(action instanceof RaiseAction)) { + return; + } + + if (!state.getTableState().isBettingOpen()) { + throw new RuleViolationException("Betting not open for raise"); + } + + if (!state.getTableState().canReopenBetting()) { + throw new RuleViolationException("Raise not allowed anymore"); + } + } +} diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java new file mode 100644 index 0000000..0e8d9d1 --- /dev/null +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/game/rules/showdown/CardsSpeakRule.java @@ -0,0 +1,101 @@ +package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.showdown; + +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card; +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.PlayerStatus; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.Rule; +import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState; +import java.util.ArrayList; +import java.util.List; + +/** + * The CardsSpeakRule class implements the Rule interface and defines the logic + * for + * determining the winner of a poker hand based on the players' hole cards and + * 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 { + + /** + * Validates the action for the showdown phase. In this implementation, the + * CardsSpeakRule does not perform any validation, as it is responsible for + * determining the winner based on the players' hands. The validation logic for + * player actions during the showdown phase should be handled by other rules. + * + * @param state The current state of the game. + * @param action The action to be validated. + */ + @Override + public void validate(GameState state, Action action) { + // No validation needed; rule is only applied at showdown + } + + /** + * Determines the winner of the poker hand by evaluating each player's hand + * rank based on their hole cards and the community cards. It compares the + * hand ranks of all active players and returns the player with the best hand. + * + * @param state The current state of the game, which includes player + * information, + * hole cards, and community cards. + * @return The player with the best hand, or null if there are no active + * players. + */ + public Player determineWinner(GameState state) { + + List players = new ArrayList<>(state.getPlayers()); + + Player bestPlayer = null; + HandRank bestRank = null; + + for (Player player : players) { + + if (player.getStatus() == PlayerStatus.FOLDED) { + continue; + } + + List cards = new ArrayList<>(); + + cards.addAll(state.getHoleCards(player.getId())); + cards.addAll(state.getCommunityCards()); + + HandRank rank = HandEvaluator.evaluate(cards); + + if (bestRank == null || rank.compareTo(bestRank) > 0) { + + bestRank = rank; + bestPlayer = player; + } + } + + return bestPlayer; + } + + /** + * Awards the pot to the winner of the poker hand. It determines the winner(s) + * using the determineWinner method, retrieves the total amount in the pot, + * and adds it to the winner's chips. Finally, it resets the pot for the next + * hand. + * + * @param state The current state of the game, which includes player information + * and pot details. + */ + public void awardPot(GameState state) { + + Player winner = determineWinner(state); + + if (winner == null) { + return; + } + + int pot = state.getPot().getAmount(); + + winner.addChips(pot); + + state.getPot().reset(); + } +}