Merge remote-tracking branch 'origin/main' into feat/gameui
This commit is contained in:
@@ -10,15 +10,15 @@ import org.apache.logging.log4j.Logger;
|
||||
/**
|
||||
* Entry point for the Casono client application. Handles client startup and connection parameters.
|
||||
*
|
||||
* <p>Standardkonstruktor für die Anwendung.
|
||||
* <p>Default constructor for the application.
|
||||
*/
|
||||
public class ClientApp {
|
||||
|
||||
private static final Logger LOGGER = LogManager.getLogger(ClientApp.class);
|
||||
|
||||
/** Standardkonstruktor. */
|
||||
/** Default constructor. */
|
||||
public ClientApp() {
|
||||
// Standardkonstruktor
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,13 +6,13 @@ import javafx.application.Application;
|
||||
/**
|
||||
* Launcher for the Casono main UI.
|
||||
*
|
||||
* <p>Standardkonstruktor für die Anwendung.
|
||||
* <p>Default constructor for the application.
|
||||
*/
|
||||
public class Launcher {
|
||||
|
||||
/** Standardkonstruktor. */
|
||||
/** Default constructor. */
|
||||
public Launcher() {
|
||||
// Standardkonstruktor
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -10,13 +10,13 @@ import javafx.stage.Stage;
|
||||
/**
|
||||
* JavaFX Application class for the Casono main UI.
|
||||
*
|
||||
* <p>Standardkonstruktor für die Anwendung.
|
||||
* <p>Default constructor for the application.
|
||||
*/
|
||||
public class Casinomainui extends Application {
|
||||
|
||||
/** Standardkonstruktor. */
|
||||
/** Default constructor. */
|
||||
public Casinomainui() {
|
||||
// Standardkonstruktor
|
||||
// Default constructor
|
||||
}
|
||||
|
||||
private static final int SCENE_WIDTH = 1200;
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ public class CasinomainuiController {
|
||||
@FXML
|
||||
public void handleCreateLobbyButton() {
|
||||
if (translationManager.isFull()) {
|
||||
LOGGER.warn("Grid voll! Keine weiteren Lobbys moeglich.");
|
||||
LOGGER.warn("Grid is full! No more lobbies available.");
|
||||
return;
|
||||
}
|
||||
int buttonId = nextButtonId++;
|
||||
@@ -68,7 +68,7 @@ public class CasinomainuiController {
|
||||
LOGGER.info("ButtonID: {}, LobbyID: {}", buttonId, lobbyId);
|
||||
gridManager.renderLobbyButtons();
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Fehler beim Hinzufügen: {}", e.getMessage());
|
||||
LOGGER.error("Error while adding lobby button: {}", e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-23
@@ -4,19 +4,19 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Verwaltet das Mapping zwischen Button-IDs und Lobby-IDs rein im Speicher. Keine Dateioperationen,
|
||||
* nur Laufzeitdatenstruktur.
|
||||
* Manages the mapping between Button IDs and Lobby IDs in memory only. No file operations,
|
||||
* runtime-only data structure.
|
||||
*/
|
||||
public class LobbyButtonTranslationManager {
|
||||
|
||||
// Singleton-Instanz
|
||||
// Singleton instance
|
||||
private static LobbyButtonTranslationManager instance;
|
||||
|
||||
// Singleton-Zugriff
|
||||
// Singleton access
|
||||
/**
|
||||
* Liefert die Singleton-Instanz des Managers.
|
||||
* Returns the singleton instance of the manager.
|
||||
*
|
||||
* @return die einzige Instanz von {@code LobbyButtonTranslationManager}
|
||||
* @return the single instance of {@code LobbyButtonTranslationManager}
|
||||
*/
|
||||
public static LobbyButtonTranslationManager getInstance() {
|
||||
if (instance == null) {
|
||||
@@ -25,32 +25,32 @@ public class LobbyButtonTranslationManager {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/** Maximale Anzahl an Buttons/Lobbys */
|
||||
/** Maximum number of buttons/lobbies */
|
||||
private static final int MAX_BUTTONS = 8;
|
||||
|
||||
/** Zuordnung ButtonID → LobbyID */
|
||||
/** Mapping ButtonID → LobbyID */
|
||||
private final Map<Integer, Integer> buttonIdToLobbyId = new HashMap<>();
|
||||
|
||||
/** Privater Konstruktor für Singleton-Pattern */
|
||||
/** Private constructor for the singleton pattern */
|
||||
private LobbyButtonTranslationManager() {
|
||||
// Zuordnung bleibt leer beim Start
|
||||
// Mapping is empty at startup
|
||||
}
|
||||
|
||||
/**
|
||||
* Prüft, ob das Grid voll ist (MAX_BUTTONS erreicht).
|
||||
* Checks whether the grid is full (MAX_BUTTONS reached).
|
||||
*
|
||||
* @return true, wenn Grid voll; sonst false
|
||||
* @return true if the grid is full; otherwise false
|
||||
*/
|
||||
public boolean isFull() {
|
||||
return buttonIdToLobbyId.size() >= MAX_BUTTONS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fügt eine Zuordnung ButtonID → LobbyID hinzu.
|
||||
* Adds a mapping ButtonID → LobbyID.
|
||||
*
|
||||
* @param buttonId Die ID des Buttons
|
||||
* @param lobbyId Die ID der Lobby
|
||||
* @throws Exception wenn das Grid voll ist
|
||||
* @param buttonId the ID of the button
|
||||
* @param lobbyId the ID of the lobby
|
||||
* @throws Exception when the grid is full
|
||||
*/
|
||||
public void addLobbyButton(int buttonId, int lobbyId) throws Exception {
|
||||
if (isFull()) {
|
||||
@@ -60,28 +60,28 @@ public class LobbyButtonTranslationManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Entfernt eine Zuordnung für die gegebene ButtonID.
|
||||
* Removes the mapping for the given ButtonID.
|
||||
*
|
||||
* @param buttonId Die ID des zu entfernenden Buttons
|
||||
* @param buttonId the ID of the button to remove
|
||||
*/
|
||||
public void removeLobbyButton(int buttonId) {
|
||||
buttonIdToLobbyId.remove(buttonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die LobbyID für eine gegebene ButtonID zurück.
|
||||
* Returns the LobbyID for a given ButtonID.
|
||||
*
|
||||
* @param buttonId Die ButtonID
|
||||
* @return Die zugehoerige LobbyID oder null, falls nicht vorhanden
|
||||
* @param buttonId the ButtonID
|
||||
* @return the associated LobbyID or null if not present
|
||||
*/
|
||||
public Integer getLobbyIdForButton(int buttonId) {
|
||||
return buttonIdToLobbyId.get(buttonId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt die gesamte Zuordnung ButtonID → LobbyID zurück.
|
||||
* Returns the full mapping ButtonID → LobbyID.
|
||||
*
|
||||
* @return Map aller Zuordnungen
|
||||
* @return Map of all mappings
|
||||
*/
|
||||
public Map<Integer, Integer> getButtonIdToLobbyId() {
|
||||
return buttonIdToLobbyId;
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick.CheckUsernameRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping.PingRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserCleanupJob;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.NetworkManager;
|
||||
@@ -7,6 +13,7 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandR
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParserDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.DisconnectEvent;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.events.EventBus;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionDisconnectJob;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.sessions.SessionManager;
|
||||
import java.time.Duration;
|
||||
@@ -58,6 +65,31 @@ public class ServerApp {
|
||||
SESSION_DISCONNECT_JOB_PERIOD,
|
||||
TimeUnit.SECONDS);
|
||||
|
||||
ResponseDispatcher responseDispatcher = new ResponseDispatcher(sessionManager);
|
||||
|
||||
registerCommands(dispatcher, router, responseDispatcher, userRegistry);
|
||||
|
||||
networkManager.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers command parsers and handlers.
|
||||
*
|
||||
* @param parserDispatcher the dispatcher responsible for parsing incoming commands
|
||||
* @param commandRouter the router that dispatches parsed commands to appropriate handlers
|
||||
* @param responseDispatcher the dispatcher responsible for sending responses back to clients
|
||||
*/
|
||||
private static void registerCommands(
|
||||
CommandParserDispatcher parserDispatcher,
|
||||
CommandRouter commandRouter,
|
||||
ResponseDispatcher responseDispatcher,
|
||||
UserRegistry userRegistry) {
|
||||
parserDispatcher.register("PING", new PingParser());
|
||||
commandRouter.register(PingRequest.class, new PingHandler(responseDispatcher));
|
||||
|
||||
parserDispatcher.register("CHECK_USERNAME", new CheckUsernameParser());
|
||||
commandRouter.register(
|
||||
CheckUsernameRequest.class,
|
||||
new CheckUsernameHandler(responseDispatcher, userRegistry));
|
||||
}
|
||||
}
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.User;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.user.UserRegistry;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
import java.util.Optional;
|
||||
|
||||
/** Handles {@link CheckUsernameRequest}s to check whether a username is available. */
|
||||
public class CheckUsernameHandler implements CommandHandler<CheckUsernameRequest> {
|
||||
private final ResponseDispatcher responseDispatcher;
|
||||
private final UserRegistry userRegistry;
|
||||
|
||||
/**
|
||||
* Creates a new handler for checking username availability.
|
||||
*
|
||||
* @param responseDispatcher the dispatcher used to send the response
|
||||
* @param userRegistry the registry used to look up existing users
|
||||
*/
|
||||
public CheckUsernameHandler(ResponseDispatcher responseDispatcher, UserRegistry userRegistry) {
|
||||
this.responseDispatcher = responseDispatcher;
|
||||
this.userRegistry = userRegistry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the username availability check for the given request.
|
||||
*
|
||||
* <p>If no user exists for the requested username, the username is reported as {@link
|
||||
* UsernameAvailability#FREE}; otherwise, it is reported as {@link UsernameAvailability#TAKEN}.
|
||||
*
|
||||
* @param request the request to execute
|
||||
*/
|
||||
@Override
|
||||
public void execute(CheckUsernameRequest request) {
|
||||
Optional<User> user = userRegistry.getByUsername(request.getUsername());
|
||||
UsernameAvailability availability;
|
||||
if (user.isEmpty()) {
|
||||
availability = UsernameAvailability.FREE;
|
||||
} else {
|
||||
availability = UsernameAvailability.TAKEN;
|
||||
}
|
||||
responseDispatcher.dispatch(new CheckUsernameResponse(request.getContext(), availability));
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.RequestParameterAccessor;
|
||||
|
||||
/** Parses a primitive request into a {@link CheckUsernameRequest}. */
|
||||
public class CheckUsernameParser implements CommandParser<CheckUsernameRequest> {
|
||||
/**
|
||||
* Extracts the required {@code USERNAME} parameter from the incoming request.
|
||||
*
|
||||
* @param primitiveRequest the request to parse
|
||||
* @return {@link CheckUsernameRequest} containing the username
|
||||
*/
|
||||
@Override
|
||||
public CheckUsernameRequest parse(PrimitiveRequest primitiveRequest) {
|
||||
RequestParameterAccessor accessor =
|
||||
new RequestParameterAccessor(primitiveRequest.parameters());
|
||||
return new CheckUsernameRequest(primitiveRequest.context(), accessor.require("USERNAME"));
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
|
||||
/** Request implementation used to check whether a username is available or already taken */
|
||||
public class CheckUsernameRequest extends Request {
|
||||
private final String username;
|
||||
|
||||
/**
|
||||
* Constructs a new CheckUsernameRequest with the given context and username to check
|
||||
*
|
||||
* @param context the {@link RequestContext} containing information for responding to the
|
||||
* request
|
||||
* @param username the username to check for availability
|
||||
*/
|
||||
public CheckUsernameRequest(RequestContext context, String username) {
|
||||
super(context);
|
||||
this.username = username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provided username in the request
|
||||
*
|
||||
* @return username to check
|
||||
*/
|
||||
public String getUsername() {
|
||||
return username;
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.SuccessResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.builder.ResponseBodyBuilder;
|
||||
|
||||
/** Response indicating the availability status of a username check. */
|
||||
public class CheckUsernameResponse extends SuccessResponse {
|
||||
/**
|
||||
* Creates a new response to respond to the username availability check to
|
||||
*
|
||||
* @param context the {@link RequestContext} associated with the request
|
||||
* @param availability the availability status of the requested username
|
||||
*/
|
||||
public CheckUsernameResponse(RequestContext context, UsernameAvailability availability) {
|
||||
super(context, new ResponseBodyBuilder().param("STATUS", availability).build());
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.check_nick;
|
||||
|
||||
/** Represents the availability status of a username */
|
||||
enum UsernameAvailability {
|
||||
/** Username is available */
|
||||
FREE,
|
||||
|
||||
/** Username is already in use */
|
||||
TAKEN
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.execution.CommandHandler;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.OkResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatcher;
|
||||
|
||||
/** Handler for {@link PingRequest}. */
|
||||
public class PingHandler implements CommandHandler<PingRequest> {
|
||||
private final ResponseDispatcher responseDispatcher;
|
||||
|
||||
/**
|
||||
* Create a new PingHandler to execute {@link PingRequest}s
|
||||
*
|
||||
* @param responseDispatcher dispatcher used to send responses back to clients
|
||||
*/
|
||||
public PingHandler(ResponseDispatcher responseDispatcher) {
|
||||
this.responseDispatcher = responseDispatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the ping request.
|
||||
*
|
||||
* @param request the ping request to handle
|
||||
*/
|
||||
@Override
|
||||
public void execute(PingRequest request) {
|
||||
responseDispatcher.dispatch(new OkResponse(request.getContext()));
|
||||
}
|
||||
}
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.command.parsing.CommandParser;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.PrimitiveRequest;
|
||||
|
||||
/**
|
||||
* Parser for the Ping command.
|
||||
*
|
||||
* <p>Converts a low-level {@link PrimitiveRequest} into a {@link PingRequest}.
|
||||
*/
|
||||
public class PingParser implements CommandParser<PingRequest> {
|
||||
/**
|
||||
* Parse the given primitive request into a {@link PingRequest}.
|
||||
*
|
||||
* @param primitiveRequest the raw request to parse
|
||||
* @return {@link PingRequest}
|
||||
*/
|
||||
@Override
|
||||
public PingRequest parse(PrimitiveRequest primitiveRequest) {
|
||||
return new PingRequest(primitiveRequest.context());
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.app.commands.ping;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
|
||||
/**
|
||||
* Represents a "PING" request sent by a client to check server availability and keep the connection
|
||||
* alive.
|
||||
*/
|
||||
public class PingRequest extends Request {
|
||||
/**
|
||||
* Constructs a new PingRequest with the given context.
|
||||
*
|
||||
* @param context the request context associated with this request
|
||||
*/
|
||||
public PingRequest(RequestContext context) {
|
||||
super(context);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.BlindAction;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.CallAction;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.FoldAction;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.RaiseAction;
|
||||
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.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.state.GamePhase;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state.GameState;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* GameController is responsible for managing the flow of the poker game. It interacts with the
|
||||
* GameEngine to process player actions and update the game state accordingly.
|
||||
*/
|
||||
public class GameController {
|
||||
|
||||
private final GameEngine engine;
|
||||
|
||||
private final List<PlayerId> players = new ArrayList<>();
|
||||
|
||||
private int dealerIndex = 0;
|
||||
|
||||
private static final int NEXT_PLAYER_OFFSET = 3;
|
||||
private static final int DEALER_OFFSET = 1;
|
||||
private static final int SMALL_BLIND_OFFSET = 1;
|
||||
private static final int BIG_BLIND_OFFSET = 2;
|
||||
private static final int SMALL_BLIND = 100;
|
||||
private static final int BIG_BLIND = 200;
|
||||
|
||||
/**
|
||||
* Initializes the GameController with a reference to the GameEngine.
|
||||
*
|
||||
* @param engine The GameEngine instance that manages the game state and logic.
|
||||
*/
|
||||
public GameController(GameEngine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the game with the specified name and initial chip count.
|
||||
*
|
||||
* @param name The name of the player to add.
|
||||
* @param chips The initial number of chips the player has.
|
||||
*/
|
||||
public void addPlayer(PlayerId name, int chips) {
|
||||
|
||||
// PlayerId id = PlayerId.of(name);
|
||||
//
|
||||
// players.add(id);
|
||||
// engine.getState().addPlayer(id, chips);
|
||||
|
||||
players.add(name);
|
||||
engine.getState().addPlayer(name, chips);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new hand by preparing the deck, setting the phase to PREFLOP, rotating the
|
||||
* dealer, dealing hole cards, posting blinds, and setting the first active player.
|
||||
*/
|
||||
public void startGame() {
|
||||
|
||||
if (engine.getState().getDeck() == null) {
|
||||
Deck deck = new Deck();
|
||||
deck.shuffle();
|
||||
engine.getState().setDeck(deck);
|
||||
}
|
||||
|
||||
engine.getState().setHandActive(true);
|
||||
engine.getState().setPhase(GamePhase.PREFLOP);
|
||||
|
||||
rotateDealer();
|
||||
dealHoleCards();
|
||||
postBlinds();
|
||||
|
||||
int nextPlayerIndex = (dealerIndex + NEXT_PLAYER_OFFSET) % players.size();
|
||||
engine.getState().setCurrentPlayerIndex(nextPlayerIndex);
|
||||
}
|
||||
|
||||
/** Rotates the dealer position to the next player in the list. */
|
||||
private void rotateDealer() {
|
||||
dealerIndex = (dealerIndex + DEALER_OFFSET) % players.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the player currently acting as dealer.
|
||||
*
|
||||
* @return The name of the current dealer.
|
||||
*/
|
||||
public PlayerId getDealer() {
|
||||
return players.get(dealerIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the small and big blind players relative to the dealer and submits the
|
||||
* corresponding blind actions to the engine.
|
||||
*/
|
||||
public void postBlinds() {
|
||||
|
||||
PlayerId smallBlind = players.get((dealerIndex + SMALL_BLIND_OFFSET) % players.size());
|
||||
PlayerId bigBlind = players.get((dealerIndex + BIG_BLIND_OFFSET) % players.size());
|
||||
|
||||
engine.processAction(new BlindAction(smallBlind, SMALL_BLIND));
|
||||
engine.processAction(new BlindAction(bigBlind, BIG_BLIND));
|
||||
}
|
||||
|
||||
/** Deals hole cards to each player from the deck. */
|
||||
public void dealHoleCards() {
|
||||
|
||||
Deck deck = engine.getState().getDeck();
|
||||
|
||||
for (PlayerId player : players) {
|
||||
|
||||
Card c1 = deck.draw();
|
||||
Card c2 = deck.draw();
|
||||
|
||||
engine.getState().giveHoleCards(player, c1, c2);
|
||||
}
|
||||
}
|
||||
|
||||
/** Draws three cards from the deck and adds them as community cards (flop). */
|
||||
public void dealFlop() {
|
||||
|
||||
Deck deck = engine.getState().getDeck();
|
||||
|
||||
engine.getState().addCommunityCard(deck.draw());
|
||||
engine.getState().addCommunityCard(deck.draw());
|
||||
engine.getState().addCommunityCard(deck.draw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals the turn by drawing one community card from the deck and adding it to the game state.
|
||||
*/
|
||||
public void dealTurn() {
|
||||
engine.getState().addCommunityCard(engine.getState().getDeck().draw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Deals the river by drawing one community card from the deck and adding it to the game state.
|
||||
*/
|
||||
public void dealRiver() {
|
||||
engine.getState().addCommunityCard(engine.getState().getDeck().draw());
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a player's fold action by sending a FoldAction to the GameEngine.
|
||||
*
|
||||
* @param playerId The ID of the player who is folding.
|
||||
*/
|
||||
public void playerFold(PlayerId playerId) {
|
||||
// engine.processAction(new FoldAction(PlayerId.of(playerId)));
|
||||
engine.processAction(new FoldAction(playerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a player's call action by sending a CallAction to the GameEngine.
|
||||
*
|
||||
* @param playerId The ID of the player who is calling.
|
||||
*/
|
||||
public void playerCall(PlayerId playerId) {
|
||||
// engine.processAction(new CallAction(PlayerId.of(playerId)));
|
||||
engine.processAction(new CallAction(playerId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a player's raise action by sending a RaiseAction to the GameEngine.
|
||||
*
|
||||
* @param playerId The ID of the player who is raising.
|
||||
* @param amount The amount the player is raising.
|
||||
*/
|
||||
public void playerRaise(PlayerId playerId, int amount) {
|
||||
// engine.processAction(new RaiseAction(PlayerId.of(playerId), amount));
|
||||
engine.processAction(new RaiseAction(playerId, amount));
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current game state from the GameEngine.
|
||||
*
|
||||
* @return The current GameState object representing the state of the game.
|
||||
*/
|
||||
public GameState getState() {
|
||||
return engine.getState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of community cards currently on the table.
|
||||
*
|
||||
* @return A list of Card objects representing the community cards.
|
||||
*/
|
||||
public List<Card> getCommunityCards() {
|
||||
return engine.getState().getCommunityCards();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Card objects
|
||||
* representing the player's hole cards.
|
||||
*/
|
||||
public Map<PlayerId, List<Card>> getPlayerCards() {
|
||||
return engine.getState().getPlayerCards();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the winner of the current hand by evaluating the best possible poker hand for each
|
||||
* active player.
|
||||
*
|
||||
* <p>The evaluation is based on the player's two hole cards combined with the five community
|
||||
* cards on the board.
|
||||
*
|
||||
* @return The ID of the winning player.
|
||||
*/
|
||||
public PlayerId determineWinner() {
|
||||
|
||||
List<Card> board = engine.getState().getCommunityCards();
|
||||
GameState state = engine.getState();
|
||||
|
||||
PlayerId bestPlayer = null;
|
||||
HandRank bestRank = null;
|
||||
|
||||
for (PlayerId player : players) {
|
||||
|
||||
if (state.isFolded(player)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Card> cards = new ArrayList<>(board);
|
||||
cards.addAll(state.getHoleCards(player));
|
||||
|
||||
HandRank rank = HandEvaluator.evaluate(cards);
|
||||
|
||||
if (bestRank == null || rank.compareTo(bestRank) > 0) {
|
||||
bestRank = rank;
|
||||
bestPlayer = player;
|
||||
}
|
||||
}
|
||||
|
||||
return bestPlayer;
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action;
|
||||
|
||||
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 game. It implements the
|
||||
* Action interface and provides a common implementation for retrieving the player ID associated
|
||||
* with the action.
|
||||
*/
|
||||
public abstract class AbstractAction implements Action {
|
||||
protected final PlayerId playerId;
|
||||
|
||||
/**
|
||||
* Constructs an AbstractAction with the specified player ID.
|
||||
*
|
||||
* @param playerId The ID of the player performing the action.
|
||||
*/
|
||||
public AbstractAction(PlayerId playerId) {
|
||||
this.playerId = playerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the player performing the action.
|
||||
*
|
||||
* @return The player ID associated with this action.
|
||||
*/
|
||||
@Override
|
||||
public PlayerId getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
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.state.GameState;
|
||||
|
||||
/**
|
||||
* The Action interface defines the structure for all player actions in the poker game. Each action
|
||||
* must specify its type, provide an implementation for executing the action on the game state, and
|
||||
* identify the player performing the action.
|
||||
*/
|
||||
public interface Action {
|
||||
ActionType getType();
|
||||
|
||||
void execute(GameState state);
|
||||
|
||||
PlayerId getPlayerId();
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action;
|
||||
|
||||
/**
|
||||
* ActionType is an enumeration that defines the various types of actions that players can perform
|
||||
* in a poker game. Each action type corresponds to a specific move or decision that a player can
|
||||
* make during their turn.
|
||||
*/
|
||||
public enum ActionType {
|
||||
FOLD,
|
||||
CALL,
|
||||
RAISE,
|
||||
CHECK,
|
||||
BET,
|
||||
ALL_IN,
|
||||
BLIND
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
/**
|
||||
* AllInAction represents the action of a player going all-in in a poker game. When a player goes
|
||||
* all-in, they bet all of their remaining chips. This action 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 {
|
||||
|
||||
/**
|
||||
* Constructs an AllInAction for the specified player ID.
|
||||
*
|
||||
* @param playerId The ID of the player performing the all-in action.
|
||||
*/
|
||||
public AllInAction(PlayerId playerId) {
|
||||
super(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this action, which is ALL_IN.
|
||||
*
|
||||
* @return The ActionType corresponding to this action.
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.ALL_IN;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the all-in action on the given game state. This method updates the player's chip
|
||||
* count, adds the bet to the pot, and updates the current bet for the player in the game state.
|
||||
*
|
||||
* @param state The current game state on which to execute the action.
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
Player player = state.getPlayer(playerId);
|
||||
int chips = player.getChips();
|
||||
|
||||
player.removeChips(chips);
|
||||
state.addToPot(chips);
|
||||
state.setCurrentBet(playerId, state.getCurrentBet(playerId) + chips);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
/**
|
||||
* Represents a bet action where a player contributes a fixed amount of chips. The amount is
|
||||
* deducted from the player's chips, added to the pot, and both the player's current bet and the
|
||||
* table's current bet are set to this amount.
|
||||
*/
|
||||
public class BetAction extends AbstractAction {
|
||||
|
||||
private final int amount;
|
||||
|
||||
/**
|
||||
* Constructs a BetAction for the specified player ID and bet amount.
|
||||
*
|
||||
* @param playerId The ID of the player performing the bet action.
|
||||
* @param amount The amount of chips the player is betting.
|
||||
*/
|
||||
public BetAction(PlayerId playerId, int amount) {
|
||||
super(playerId);
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the amount of chips being bet in this action.
|
||||
*
|
||||
* @return The amount of chips being bet.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this action, which is BET.
|
||||
*
|
||||
* @return The ActionType corresponding to this action.
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.BET;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the bet action on the given game state. This method updates the player's chip count,
|
||||
* adds the bet to the pot, and updates the current bet for the player in the game state.
|
||||
*
|
||||
* @param state The current game state on which to execute the action.
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
|
||||
Player player = state.getPlayer(playerId);
|
||||
|
||||
player.removeChips(amount);
|
||||
|
||||
state.addToPot(amount);
|
||||
state.setCurrentBet(playerId, amount);
|
||||
|
||||
state.getTableState().setCurrentBet(amount);
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
/**
|
||||
* Executes a blind by deducting chips from the player, adding them to the pot, increasing the
|
||||
* player's current bet, and updating the table's current bet if the blind exceeds the existing bet.
|
||||
*/
|
||||
public class BlindAction implements Action {
|
||||
|
||||
private final PlayerId playerId;
|
||||
private final int amount;
|
||||
|
||||
/**
|
||||
* Constructs a BlindAction for the specified player ID and blind amount.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public BlindAction(PlayerId playerId, int amount) {
|
||||
this.playerId = playerId;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this action, which is BLIND.
|
||||
*
|
||||
* @return The ActionType corresponding to this action.
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.BLIND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the player performing the action.
|
||||
*
|
||||
* @return The player ID associated with this action.
|
||||
*/
|
||||
@Override
|
||||
public PlayerId getPlayerId() {
|
||||
return playerId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the blind action on the given game state. This method updates the player's chip
|
||||
* count, adds the blind bet to the pot, and updates the current bet for the player in the game
|
||||
* state.
|
||||
*
|
||||
* @param state The current game state on which to execute the action.
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
|
||||
Player player = state.getPlayer(playerId);
|
||||
|
||||
player.removeChips(amount);
|
||||
|
||||
state.addToPot(amount);
|
||||
|
||||
int alreadyPaid = state.getCurrentBet(playerId);
|
||||
|
||||
state.setCurrentBet(playerId, alreadyPaid + amount);
|
||||
|
||||
if (state.getTableState().getCurrentBet() < amount) {
|
||||
state.getTableState().setCurrentBet(amount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the amount of chips being posted as a blind bet in this action.
|
||||
*
|
||||
* @return The amount of chips being posted as a blind bet.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
/**
|
||||
* CallAction represents the action of a player calling in a poker game. When a player calls, they
|
||||
* match the current bet on the table by paying the difference between their current bet and the
|
||||
* table's current bet. This action updates the player's chip count, adds the required amount to the
|
||||
* pot, and updates the player's current bet accordingly.
|
||||
*/
|
||||
public class CallAction extends AbstractAction {
|
||||
|
||||
/**
|
||||
* Constructs a CallAction for the specified player.
|
||||
*
|
||||
* @param playerId the ID of the player performing the call action
|
||||
*/
|
||||
public CallAction(PlayerId playerId) {
|
||||
super(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the type of this action, which is CALL.
|
||||
*
|
||||
* @return the ActionType representing a call action
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.CALL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the call action on the given game state. This method checks if the player is folded,
|
||||
* calculates the amount to call, updates the player's chip count, adds the amount to the pot,
|
||||
* and updates the player's current bet.
|
||||
*
|
||||
* @param state the current game state on which to execute the call action
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
|
||||
Player player = state.getPlayer(playerId);
|
||||
|
||||
if (player.isFolded()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int tableBet = state.getTableState().getCurrentBet();
|
||||
int playerBet = state.getCurrentBet(playerId);
|
||||
|
||||
int toCall = tableBet - playerBet;
|
||||
|
||||
if (toCall <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
player.removeChips(toCall);
|
||||
state.addToPot(toCall);
|
||||
|
||||
state.setCurrentBet(playerId, playerBet + toCall);
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
/**
|
||||
* FoldAction represents the action of a player folding in a poker game. When a player folds, they
|
||||
* forfeit their hand and are no longer active in the current round. This action updates the
|
||||
* player's status to indicate that they have folded.
|
||||
*/
|
||||
public class FoldAction extends AbstractAction {
|
||||
|
||||
/**
|
||||
* Constructs a FoldAction for the specified player ID.
|
||||
*
|
||||
* @param playerId The ID of the player performing the fold action.
|
||||
*/
|
||||
public FoldAction(PlayerId playerId) {
|
||||
super(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this action, which is FOLD.
|
||||
*
|
||||
* @return The ActionType corresponding to this action.
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.FOLD;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the fold action on the given game state. This method updates the player's status to
|
||||
* indicate that they have folded.
|
||||
*
|
||||
* @param state The current game state on which to execute the action.
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
Player player = state.getPlayer(playerId);
|
||||
player.setFolded(true);
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.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.state.GameState;
|
||||
|
||||
public class RaiseAction extends AbstractAction {
|
||||
|
||||
private final int raiseAmount;
|
||||
|
||||
/**
|
||||
* Constructs a RaiseAction for the specified player ID and raise amount.
|
||||
*
|
||||
* @param playerId The ID of the player performing the raise action.
|
||||
* @param raiseAmount The amount of chips the player is raising.
|
||||
*/
|
||||
public RaiseAction(PlayerId playerId, int raiseAmount) {
|
||||
super(playerId);
|
||||
this.raiseAmount = raiseAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the amount of chips being raised in this action.
|
||||
*
|
||||
* @return The amount of chips being raised.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return raiseAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of this action, which is RAISE.
|
||||
*
|
||||
* @return The ActionType corresponding to this action.
|
||||
*/
|
||||
@Override
|
||||
public ActionType getType() {
|
||||
return ActionType.RAISE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the raise action on the given game state. This method updates the player's chip
|
||||
* count, adds the raise amount to the pot, and updates the current bet for the player in the
|
||||
* game state.
|
||||
*
|
||||
* @param state The current game state on which to execute the action.
|
||||
*/
|
||||
@Override
|
||||
public void execute(GameState state) {
|
||||
|
||||
Player player = state.getPlayer(playerId);
|
||||
|
||||
int alreadyPaid = state.getCurrentBet(playerId);
|
||||
int toCall = state.getTableState().getCurrentBet() - alreadyPaid;
|
||||
|
||||
int total = toCall + raiseAmount;
|
||||
|
||||
player.removeChips(total);
|
||||
state.addToPot(total);
|
||||
|
||||
int newBet = state.getTableState().getCurrentBet() + raiseAmount;
|
||||
|
||||
state.getTableState().setCurrentBet(newBet);
|
||||
state.setCurrentBet(playerId, alreadyPaid + total);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
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. Each card has a suit
|
||||
* (Hearts, Diamonds, Clubs, Spades) and a rank (2-10, Jack, Queen, King, Ace). This class provides
|
||||
* methods to retrieve the suit and rank of the card, as well as a string representation of the
|
||||
* card.
|
||||
*/
|
||||
public class Card {
|
||||
|
||||
private Suit suit;
|
||||
private Rank rank;
|
||||
|
||||
/**
|
||||
* Constructs a Card with the specified suit and rank.
|
||||
*
|
||||
* @param suit The suit of the card (Hearts, Diamonds, Clubs, Spades).
|
||||
* @param rank The rank of the card (2-10, Jack, Queen, King, Ace).
|
||||
*/
|
||||
public Card(Suit suit, Rank rank) {
|
||||
this.suit = suit;
|
||||
this.rank = rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the suit of the card.
|
||||
*
|
||||
* @return The suit of the card.
|
||||
*/
|
||||
public Suit getSuit() {
|
||||
return suit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the rank of the card.
|
||||
*
|
||||
* @return The rank of the card.
|
||||
*/
|
||||
public Rank getRank() {
|
||||
return rank;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of the card, combining its rank and suit.
|
||||
*
|
||||
* @return A string representation of the card (e.g., "Ace of Spades").
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return rank + " of " + suit;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Deck class represents a standard deck of playing cards. It provides functionality to create a
|
||||
* full deck of 52 cards, shuffle the deck, and draw cards from it. The deck is initialized with all
|
||||
* combinations of suits and ranks, and can be manipulated through its methods.
|
||||
*/
|
||||
public class Deck {
|
||||
|
||||
private List<Card> cards;
|
||||
|
||||
/**
|
||||
* Constructs a new Deck object and initializes it with a standard set of 52 playing cards. The
|
||||
* deck is created by iterating through all suits and ranks, creating a Card object for each
|
||||
* combination, and adding it to the list of cards.
|
||||
*/
|
||||
public Deck() {
|
||||
|
||||
cards = new ArrayList<>();
|
||||
|
||||
for (Suit suit : Suit.values()) {
|
||||
for (Rank rank : Rank.values()) {
|
||||
cards.add(new Card(suit, rank));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shuffles the deck of cards using the Collections.shuffle method, which randomly permutes the
|
||||
* list of cards. This method can be called to randomize the order of the cards in the deck
|
||||
* before drawing.
|
||||
*/
|
||||
public void shuffle() {
|
||||
Collections.shuffle(cards);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws and removes the last card in the list, which represents the top of the deck. This
|
||||
* method removes and returns the last card in the list of cards, which represents the top of
|
||||
* the deck. If the deck is empty, this method will throw an IndexOutOfBoundsException.
|
||||
*
|
||||
* @return The Card object that was drawn from the deck.
|
||||
*/
|
||||
public Card draw() {
|
||||
return cards.remove(cards.size() - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the cards of the deck. This method replaces the internal list
|
||||
* with a copy of the provided list.
|
||||
*
|
||||
* @param cards the new list of cards to set
|
||||
*/
|
||||
public void setCards(List<Card> cards) {
|
||||
this.cards = new LinkedList<>(cards);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
|
||||
|
||||
/**
|
||||
* The Rank enumeration represents the rank of a playing card in a standard deck of cards. It
|
||||
* includes the ranks from Two to Ace, which are used to determine the value of the card in various
|
||||
* card games, including poker.
|
||||
*/
|
||||
public enum Rank {
|
||||
TWO,
|
||||
THREE,
|
||||
FOUR,
|
||||
FIVE,
|
||||
SIX,
|
||||
SEVEN,
|
||||
EIGHT,
|
||||
NINE,
|
||||
TEN,
|
||||
JACK,
|
||||
QUEEN,
|
||||
KING,
|
||||
ACE
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck;
|
||||
|
||||
/**
|
||||
* The Suit enumeration represents the four suits of a standard deck of playing cards: Hearts,
|
||||
* Diamonds, Clubs, and Spades. Each suit is used to categorize the cards and can have different
|
||||
* values in various card games, including poker.
|
||||
*/
|
||||
public enum Suit {
|
||||
HEARTS,
|
||||
DIAMONDS,
|
||||
CLUBS,
|
||||
SPADES
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.action.Action;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.rules.RuleEngine;
|
||||
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 game. It integrates
|
||||
* the RuleEngine, RoundManager, and TurnManager to orchestrate the flow of the game. The GameEngine
|
||||
* processes player actions, updates the game state accordingly, and ensures that the game
|
||||
* progresses through its various stages (pre-flop, flop, turn, river) while adhering to the rules
|
||||
* of poker.
|
||||
*/
|
||||
public class GameEngine {
|
||||
|
||||
private final RuleEngine ruleEngine;
|
||||
private final RoundManager roundManager;
|
||||
private final TurnManager turnManager;
|
||||
|
||||
private final GameState state;
|
||||
|
||||
/**
|
||||
* Constructs a GameEngine with the specified game state, rule engine, round manager, and turn
|
||||
* manager.
|
||||
*
|
||||
* @param state The initial game state to be managed by the engine.
|
||||
* @param ruleEngine The RuleEngine instance responsible for validating player actions.
|
||||
* @param roundManager The RoundManager instance responsible for managing the progression of
|
||||
* rounds.
|
||||
* @param turnManager The TurnManager instance responsible for managing player turns.
|
||||
*/
|
||||
public GameEngine(
|
||||
GameState state,
|
||||
RuleEngine ruleEngine,
|
||||
RoundManager roundManager,
|
||||
TurnManager turnManager) {
|
||||
this.state = state;
|
||||
this.ruleEngine = ruleEngine;
|
||||
this.roundManager = roundManager;
|
||||
this.turnManager = turnManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new hand with the provided game state. This method initializes the round manager to
|
||||
* begin a new hand and sets up the game state accordingly.
|
||||
*
|
||||
* @param state The game state to be used for starting the new hand.
|
||||
*/
|
||||
public void startNewHand(GameState state) {
|
||||
roundManager.startNewHand(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a new hand using the current game state. This method is a convenience method that
|
||||
* calls the startNewHand method with the current state of the game.
|
||||
*/
|
||||
public void startNewHand() {
|
||||
roundManager.startNewHand(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a player action by validating it against the game rules, executing the action,
|
||||
* progressing to the next player's turn, and checking if the round should progress to the next
|
||||
* stage.
|
||||
*
|
||||
* @param action The player action to be processed.
|
||||
*/
|
||||
public void processAction(Action action) {
|
||||
handleAction(state, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles a player action by performing the following steps: 1. Validates the action against
|
||||
* the game rules using the RuleEngine. 2. Executes the action, which updates the game state
|
||||
* accordingly. 3. Advances to the next player's turn using the TurnManager. 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 action The player action to be processed.
|
||||
*/
|
||||
public void handleAction(GameState state, Action action) {
|
||||
|
||||
// 1.Check the rules
|
||||
ruleEngine.validate(state, action);
|
||||
|
||||
// 2. Perform action
|
||||
action.execute(state);
|
||||
|
||||
// 3. Next turn
|
||||
turnManager.nextPlayer(state);
|
||||
|
||||
// 4. Check loop logic
|
||||
roundManager.progressIfNeeded(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current game state managed by the GameEngine.
|
||||
*
|
||||
* @return The current GameState instance.
|
||||
*/
|
||||
public GameState getState() {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine;
|
||||
|
||||
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.state.GameState;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* RoundManager is responsible for managing the flow of a poker game round. It handles the
|
||||
* progression of the game through its various phases (pre-flop, flop, turn, river) and manages
|
||||
* player actions such as posting blinds and dealing cards. The RoundManager ensures that the game
|
||||
* state is updated correctly based on player actions and the current phase of the game.
|
||||
*/
|
||||
public class RoundManager {
|
||||
|
||||
public static final int SMALL_BLIND = 100;
|
||||
public static final int BIG_BLIND = 200;
|
||||
|
||||
/**
|
||||
* Starts a new hand by initializing the game state, dealing cards to players, and posting
|
||||
* blinds. This method sets the hand as active and resets any necessary state variables to
|
||||
* prepare for a new round of play.
|
||||
*
|
||||
* @param state The game state to be used for starting the new hand.
|
||||
*/
|
||||
public void startNewHand(GameState state) {
|
||||
state.setHandActive(true);
|
||||
state.resetBets();
|
||||
|
||||
dealCards(state);
|
||||
|
||||
postBlinds(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the betting round is finished and advances the game phase if necessary. This method
|
||||
* evaluates the current bets of all active players and determines if the betting round can be
|
||||
* concluded. If all players have met the current bet or are all-in, the game phase is advanced
|
||||
* to the next stage.
|
||||
*
|
||||
* @param state The current game state to be evaluated for betting round progression.
|
||||
*/
|
||||
public void progressIfNeeded(GameState state) {
|
||||
|
||||
if (isBettingRoundFinished(state)) {
|
||||
advancePhase(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the betting round is finished by checking if all active players have met the
|
||||
* current bet or are all-in. This method iterates through all players in the game state and
|
||||
* evaluates their bets against the current bet on the table.
|
||||
*
|
||||
* @param state The current game state to be evaluated for betting round completion.
|
||||
* @return true if the betting round is finished, false otherwise.
|
||||
*/
|
||||
private boolean isBettingRoundFinished(GameState state) {
|
||||
|
||||
int target = state.getTableState().getCurrentBet();
|
||||
|
||||
for (Player p : state.getPlayers()) {
|
||||
|
||||
if (p.getStatus() == PlayerStatus.FOLDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int bet = state.getCurrentBet(p.getId());
|
||||
|
||||
// The player must have placed at least one bet
|
||||
if (bet < target && !p.isAllIn()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances the game phase to the next stage (flop, turn, river, or showdown) based on the
|
||||
* current phase of the game. This method is called when the betting round is finished and
|
||||
* updates the game state accordingly to reflect the new phase of play.
|
||||
*
|
||||
* @param state The current game state to be updated with the new phase.
|
||||
*/
|
||||
private void advancePhase(GameState state) {
|
||||
|
||||
switch (state.getPhase()) {
|
||||
case PREFLOP -> dealFlop(state);
|
||||
case FLOP -> dealTurn(state);
|
||||
case TURN -> dealRiver(state);
|
||||
case RIVER -> showdown(state);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the posting of blinds at the start of a new hand. This method identifies the players
|
||||
* responsible for posting the small and big blinds, updates their chip counts, adds the blind
|
||||
* amounts to the pot, and updates the current bets for those players in the game state.
|
||||
*
|
||||
* @param state The current game state to be updated with the posted blinds.
|
||||
*/
|
||||
private void postBlinds(GameState state) {
|
||||
List<Player> playerList = new ArrayList<>(state.getPlayers());
|
||||
|
||||
Player sb = playerList.get(0);
|
||||
Player bb = playerList.get(1);
|
||||
|
||||
int smallBlind = SMALL_BLIND;
|
||||
int bigBlind = BIG_BLIND;
|
||||
|
||||
sb.removeChips(smallBlind);
|
||||
bb.removeChips(bigBlind);
|
||||
|
||||
state.addToPot(smallBlind + bigBlind);
|
||||
|
||||
state.setCurrentBet(sb.getId(), smallBlind);
|
||||
state.setCurrentBet(bb.getId(), bigBlind);
|
||||
|
||||
state.getTableState().setCurrentBet(bigBlind);
|
||||
}
|
||||
|
||||
private void dealCards(GameState state) {}
|
||||
|
||||
private void dealFlop(GameState state) {}
|
||||
|
||||
private void dealTurn(GameState state) {}
|
||||
|
||||
private void dealRiver(GameState state) {}
|
||||
|
||||
private void showdown(GameState state) {}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.engine;
|
||||
|
||||
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 poker game. It provides
|
||||
* functionality to determine the next player in the sequence and update the game state accordingly.
|
||||
* The TurnManager ensures that players take their turns in the correct order, allowing for a smooth
|
||||
* and organized gameplay experience.
|
||||
*/
|
||||
public class TurnManager {
|
||||
|
||||
/**
|
||||
* Advances the game state to the next player's turn. This method calculates the index of the
|
||||
* next player based on the current player index and the total number of players in the game. It
|
||||
* then updates the game state to reflect the new current player.
|
||||
*
|
||||
* @param state The current game state that will be updated to reflect the next player's turn.
|
||||
*/
|
||||
public void nextPlayer(GameState state) {
|
||||
|
||||
int next = (state.getCurrentPlayerIndex() + 1) % state.getPlayers().size();
|
||||
|
||||
state.setCurrentPlayerIndex(next);
|
||||
}
|
||||
}
|
||||
+378
@@ -0,0 +1,378 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Suit;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The HandEvaluator class provides functionality to evaluate a poker hand and determine its rank.
|
||||
* It analyzes a list of cards and identifies the best possible hand according to standard poker
|
||||
* rules, such as flushes, straights, pairs, and more. The evaluation process involves counting card
|
||||
* ranks, grouping by suits, and checking for specific hand combinations to assign the correct hand
|
||||
* rank.
|
||||
*/
|
||||
public class HandEvaluator {
|
||||
private static final int RANK_OFFSET = 2;
|
||||
private static final int MIN_FLUSH_SIZE = 5;
|
||||
private static final int ROYAL_FLUSH_ACE = 14;
|
||||
private static final int ROYAL_FLUSH_KING = 13;
|
||||
private static final int ROYAL_FLUSH_QUEEN = 12;
|
||||
private static final int ROYAL_FLUSH_JACK = 11;
|
||||
private static final int ROYAL_FLUSH_TEN = 10;
|
||||
private static final long FOUR_OF_A_KIND_COUNT = 4L;
|
||||
private static final int FOUR_OF_A_KIND = 4;
|
||||
private static final int THREE_OF_A_KIND = 3;
|
||||
private static final int PAIR = 2;
|
||||
private static final int FIRST_INDEX = 0;
|
||||
private static final int CARD_VALUE_OFFSET = 2;
|
||||
private static final int HAND_SIZE = 5;
|
||||
private static final long THREE_OF_A_KIND_COUNT = 3L;
|
||||
private static final int THREE_OF_A_KIND_RANK = 3;
|
||||
private static final int ONE_PAIR = 1;
|
||||
private static final int DEFAULT_VALUE = 0;
|
||||
private static final int STRAIGHT_LENGTH = 5;
|
||||
private static final int INITIAL_STREAK = 1;
|
||||
private static final int START_INDEX = 1;
|
||||
private static final int CARD_DIFFERENCE = 1;
|
||||
private static final int PREVIOUS_INDEX_OFFSET = -1;
|
||||
|
||||
/**
|
||||
* Evaluates a list of cards and determines the best possible hand rank.
|
||||
*
|
||||
* @param cards A list of Card objects representing the player's hand.
|
||||
* @return A HandRank object representing the evaluated hand rank.
|
||||
*/
|
||||
public static HandRank evaluate(List<Card> cards) {
|
||||
|
||||
List<Integer> ranks = getSortedRanks(cards);
|
||||
Map<Integer, Long> rankCount = getRankCount(ranks);
|
||||
Map<Suit, List<Card>> suits = groupBySuit(cards);
|
||||
|
||||
List<Card> flushCards = getFlushCards(suits);
|
||||
boolean flush = flushCards != null;
|
||||
boolean straight = isStraight(ranks);
|
||||
|
||||
HandRank straightFlush = checkStraightFlush(flushCards);
|
||||
if (straightFlush != null) {
|
||||
return straightFlush;
|
||||
}
|
||||
|
||||
HandRank fourKind = checkFourOfAKind(rankCount);
|
||||
if (fourKind != null) {
|
||||
return fourKind;
|
||||
}
|
||||
|
||||
HandRank fullHouse = checkFullHouse(rankCount);
|
||||
if (fullHouse != null) {
|
||||
return fullHouse;
|
||||
}
|
||||
|
||||
if (flush) {
|
||||
return buildFlush(flushCards);
|
||||
}
|
||||
|
||||
if (straight) {
|
||||
return new HandRank(HandRank.Type.STRAIGHT, ranks);
|
||||
}
|
||||
|
||||
HandRank threeKind = checkThreeOfAKind(rankCount);
|
||||
if (threeKind != null) {
|
||||
return threeKind;
|
||||
}
|
||||
|
||||
HandRank twoPair = checkTwoPair(rankCount);
|
||||
if (twoPair != null) {
|
||||
return twoPair;
|
||||
}
|
||||
|
||||
HandRank onePair = checkOnePair(rankCount);
|
||||
if (onePair != null) {
|
||||
return onePair;
|
||||
}
|
||||
|
||||
return new HandRank(HandRank.Type.HIGH_CARD, ranks.stream().limit(HAND_SIZE).toList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to extract and sort the ranks of the cards in descending order.
|
||||
*
|
||||
* @param cards A list of Card objects representing the player's hand.
|
||||
* @return A sorted list of integer ranks corresponding to the cards.
|
||||
*/
|
||||
private static List<Integer> getSortedRanks(List<Card> cards) {
|
||||
return cards.stream()
|
||||
.map(c -> c.getRank().ordinal() + RANK_OFFSET)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return A map where the key is the card rank and the value is the count of occurrences.
|
||||
*/
|
||||
private static Map<Integer, Long> getRankCount(List<Integer> ranks) {
|
||||
return ranks.stream().collect(Collectors.groupingBy(r -> r, Collectors.counting()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to group the cards by their suits.
|
||||
*
|
||||
* @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 belonging to that
|
||||
* suit.
|
||||
*/
|
||||
private static Map<Suit, List<Card>> groupBySuit(List<Card> cards) {
|
||||
return cards.stream().collect(Collectors.groupingBy(Card::getSuit));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to identify if there is a flush in the hand and return the corresponding cards.
|
||||
*
|
||||
* @param suits A map where the key is the Suit and the value is a list of Cards belonging to
|
||||
* that suit.
|
||||
* @return A list of Card objects that form a flush, or null if no flush is found.
|
||||
*/
|
||||
private static List<Card> getFlushCards(Map<Suit, List<Card>> suits) {
|
||||
return suits.values().stream()
|
||||
.filter(s -> s.size() >= MIN_FLUSH_SIZE)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to build a HandRank object for a flush hand.
|
||||
*
|
||||
* @param flushCards A list of Card objects that form a flush.
|
||||
* @return A HandRank object representing the flush hand rank.
|
||||
*/
|
||||
private static HandRank buildFlush(List<Card> flushCards) {
|
||||
List<Integer> flushRanks =
|
||||
flushCards.stream()
|
||||
.map(c -> c.getRank().ordinal() + CARD_VALUE_OFFSET)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.limit(HAND_SIZE)
|
||||
.toList();
|
||||
|
||||
return new HandRank(HandRank.Type.FLUSH, flushRanks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return A HandRank object representing the straight flush or royal flush hand rank, or null
|
||||
* if neither is found.
|
||||
*/
|
||||
private static HandRank checkStraightFlush(List<Card> flushCards) {
|
||||
if (flushCards == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Integer> flushRanks =
|
||||
flushCards.stream()
|
||||
.map(c -> c.getRank().ordinal() + RANK_OFFSET)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.toList();
|
||||
|
||||
if (!isStraight(flushRanks)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (flushRanks.containsAll(
|
||||
List.of(
|
||||
ROYAL_FLUSH_ACE,
|
||||
ROYAL_FLUSH_KING,
|
||||
ROYAL_FLUSH_QUEEN,
|
||||
ROYAL_FLUSH_JACK,
|
||||
ROYAL_FLUSH_TEN))) {
|
||||
|
||||
return new HandRank(HandRank.Type.ROYAL_FLUSH, flushRanks);
|
||||
}
|
||||
|
||||
return new HandRank(HandRank.Type.STRAIGHT_FLUSH, flushRanks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 count of
|
||||
* occurrences.
|
||||
* @return A HandRank object representing the four of a kind hand rank, or null if not found.
|
||||
*/
|
||||
private static HandRank checkFourOfAKind(Map<Integer, Long> rankCount) {
|
||||
if (!rankCount.containsValue(FOUR_OF_A_KIND_COUNT)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int quad = getRank(rankCount, FOUR_OF_A_KIND);
|
||||
|
||||
return new HandRank(HandRank.Type.FOUR_OF_A_KIND, List.of(quad));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 count of
|
||||
* occurrences.
|
||||
* @return A HandRank object representing the full house hand rank, or null if not found.
|
||||
*/
|
||||
private static HandRank checkFullHouse(Map<Integer, Long> rankCount) {
|
||||
|
||||
List<Integer> trips =
|
||||
rankCount.entrySet().stream()
|
||||
.filter(e -> e.getValue() >= THREE_OF_A_KIND)
|
||||
.map(Map.Entry::getKey)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.toList();
|
||||
|
||||
List<Integer> pairs =
|
||||
rankCount.entrySet().stream()
|
||||
.filter(e -> e.getValue() >= PAIR)
|
||||
.map(Map.Entry::getKey)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.toList();
|
||||
|
||||
if (trips.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int three = trips.get(FIRST_INDEX);
|
||||
|
||||
Integer pair = pairs.stream().filter(p -> p != three).findFirst().orElse(null);
|
||||
|
||||
if (pair == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new HandRank(HandRank.Type.FULL_HOUSE, List.of(three, pair));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 count of
|
||||
* occurrences.
|
||||
* @return A HandRank object representing the three of a kind hand rank, or null if not found.
|
||||
*/
|
||||
private static HandRank checkThreeOfAKind(Map<Integer, Long> rankCount) {
|
||||
if (!rankCount.containsValue(THREE_OF_A_KIND_COUNT)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int tripsRank = getRank(rankCount, THREE_OF_A_KIND_RANK);
|
||||
|
||||
return new HandRank(HandRank.Type.THREE_OF_A_KIND, List.of(tripsRank));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 count of
|
||||
* occurrences.
|
||||
* @return A HandRank object representing the two pair hand rank, or null if not found.
|
||||
*/
|
||||
private static HandRank checkTwoPair(Map<Integer, Long> rankCount) {
|
||||
|
||||
long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count();
|
||||
|
||||
if (pairCount < PAIR) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Integer> pairRanks =
|
||||
rankCount.entrySet().stream()
|
||||
.filter(e -> e.getValue() == PAIR)
|
||||
.map(Map.Entry::getKey)
|
||||
.sorted(Comparator.reverseOrder())
|
||||
.limit(PAIR)
|
||||
.toList();
|
||||
|
||||
return new HandRank(HandRank.Type.TWO_PAIR, pairRanks);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 count of
|
||||
* occurrences.
|
||||
* @return A HandRank object representing the one pair hand rank, or null if not found.
|
||||
*/
|
||||
private static HandRank checkOnePair(Map<Integer, Long> rankCount) {
|
||||
|
||||
long pairCount = rankCount.values().stream().filter(v -> v == PAIR).count();
|
||||
|
||||
if (pairCount != ONE_PAIR) {
|
||||
return null;
|
||||
}
|
||||
|
||||
int pair = getRank(rankCount, PAIR);
|
||||
|
||||
return new HandRank(HandRank.Type.ONE_PAIR, List.of(pair));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to get the rank of a card based on the count of occurrences in the hand.
|
||||
*
|
||||
* @param map A map where the key is the card rank and the value is the count of occurrences.
|
||||
* @param count The specific count to look for (e.g., 2 for pairs, 3 for three 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) {
|
||||
|
||||
return map.entrySet().stream()
|
||||
.filter(e -> e.getValue() == count)
|
||||
.map(Map.Entry::getKey)
|
||||
.max(Integer::compare)
|
||||
.orElse(DEFAULT_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if a list of card ranks forms a straight.
|
||||
*
|
||||
* @param ranks A list of integer ranks representing the cards in the hand.
|
||||
* @return True if the ranks form a straight, false otherwise.
|
||||
*/
|
||||
private static boolean isStraight(List<Integer> ranks) {
|
||||
|
||||
Set<Integer> unique = new HashSet<>(ranks);
|
||||
|
||||
// Wheel Straight A-2-3-4-5
|
||||
if (unique.containsAll(
|
||||
List.of(
|
||||
ROYAL_FLUSH_ACE,
|
||||
ROYAL_FLUSH_KING,
|
||||
ROYAL_FLUSH_QUEEN,
|
||||
ROYAL_FLUSH_JACK,
|
||||
ROYAL_FLUSH_TEN))) {
|
||||
return true;
|
||||
}
|
||||
|
||||
List<Integer> sorted = unique.stream().sorted().collect(Collectors.toList());
|
||||
|
||||
int streak = INITIAL_STREAK;
|
||||
|
||||
for (int i = START_INDEX; i < sorted.size(); i++) {
|
||||
|
||||
if (sorted.get(i) == sorted.get(i + PREVIOUS_INDEX_OFFSET) + CARD_DIFFERENCE) {
|
||||
|
||||
streak++;
|
||||
|
||||
if (streak >= STRAIGHT_LENGTH) {
|
||||
return true;
|
||||
}
|
||||
|
||||
} else {
|
||||
streak = INITIAL_STREAK;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.evaluator;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* HandRank represents the rank of a poker hand, including its type (e.g., flush, straight) and the
|
||||
* kickers used for tie-breaking. It implements the Comparable interface to allow for easy
|
||||
* comparison between different hand ranks.
|
||||
*/
|
||||
public class HandRank implements Comparable<HandRank> {
|
||||
|
||||
/**
|
||||
* The Type enumeration defines the different types of poker hands, each with an associated
|
||||
* strength value for comparison purposes.
|
||||
*/
|
||||
public enum Type {
|
||||
HIGH_CARD(1),
|
||||
ONE_PAIR(2),
|
||||
TWO_PAIR(3),
|
||||
THREE_OF_A_KIND(4),
|
||||
STRAIGHT(5),
|
||||
FLUSH(6),
|
||||
FULL_HOUSE(7),
|
||||
FOUR_OF_A_KIND(8),
|
||||
STRAIGHT_FLUSH(9),
|
||||
ROYAL_FLUSH(10);
|
||||
|
||||
private final int strength;
|
||||
|
||||
Type(int strength) {
|
||||
this.strength = strength;
|
||||
}
|
||||
|
||||
public int getStrength() {
|
||||
return strength;
|
||||
}
|
||||
}
|
||||
|
||||
private final Type type;
|
||||
private final List<Integer> kickers;
|
||||
|
||||
/**
|
||||
* Constructs a HandRank with the specified type and kickers.
|
||||
*
|
||||
* @param type The type of the hand (e.g., flush, straight).
|
||||
* @param kickers A list of integers representing the kickers for tie-breaking.
|
||||
*/
|
||||
public HandRank(Type type, List<Integer> kickers) {
|
||||
this.type = type;
|
||||
this.kickers = kickers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the type of the hand.
|
||||
*
|
||||
* @return The type of the hand (e.g., flush, straight).
|
||||
*/
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the list of kickers for tie-breaking.
|
||||
*
|
||||
* @return A list of integers representing the kickers for tie-breaking.
|
||||
*/
|
||||
public List<Integer> getKickers() {
|
||||
return kickers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this HandRank with another HandRank for ordering. The comparison is based first on
|
||||
* the type of the hand and then on the kickers for tie-breaking.
|
||||
*
|
||||
* @param other The other HandRank to compare against.
|
||||
* @return A negative integer, zero, or a positive integer as this HandRank is less than, equal
|
||||
* to, or greater than the specified HandRank.
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(HandRank other) {
|
||||
|
||||
int typeCompare = Integer.compare(this.type.getStrength(), other.type.getStrength());
|
||||
|
||||
if (typeCompare != 0) {
|
||||
return typeCompare;
|
||||
}
|
||||
|
||||
for (int i = 0; i < Math.min(kickers.size(), other.kickers.size()); i++) {
|
||||
|
||||
int cmp = Integer.compare(kickers.get(i), other.kickers.get(i));
|
||||
|
||||
if (cmp != 0) {
|
||||
return cmp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,150 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player;
|
||||
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.deck.Card;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The Player class represents a participant in the poker game. It holds information about the
|
||||
* player's identity, chip count, status, and hand of cards. The class provides methods for managing
|
||||
* the player's chips, hand, and status during the game.
|
||||
*/
|
||||
public class Player {
|
||||
|
||||
private PlayerId id;
|
||||
private int chips;
|
||||
private PlayerStatus status;
|
||||
private boolean folded = false;
|
||||
|
||||
private List<Card> hand = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Constructs a Player with the specified ID and initial chip count. The player's status is set
|
||||
* to ACTIVE by default.
|
||||
*
|
||||
* @param id The unique identifier for the player.
|
||||
* @param chips The initial number of chips the player has.
|
||||
*/
|
||||
public Player(PlayerId id, int chips) {
|
||||
this.id = id;
|
||||
this.chips = chips;
|
||||
this.status = PlayerStatus.ACTIVE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player is all-in, meaning they have no chips left to bet.
|
||||
*
|
||||
* @return true if the player is all-in, false otherwise.
|
||||
*/
|
||||
public boolean isAllIn() {
|
||||
return chips == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the unique identifier of the player.
|
||||
*
|
||||
* @return The player's ID.
|
||||
*/
|
||||
public PlayerId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of the player. Currently identical to the player ID.
|
||||
*
|
||||
* @return The player's name.
|
||||
*/
|
||||
public String getName() {
|
||||
return id.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current chip count of the player.
|
||||
*
|
||||
* @return The number of chips the player has.
|
||||
*/
|
||||
public int getChips() {
|
||||
return chips;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specified amount of chips from the player's total. If the amount exceeds the
|
||||
* player's current chips, it sets the chip count to zero.
|
||||
*
|
||||
* @param amount The number of chips to remove from the player.
|
||||
*/
|
||||
public void removeChips(int amount) {
|
||||
chips -= amount;
|
||||
if (chips < 0) {
|
||||
chips = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a specified amount of chips to the player's total.
|
||||
*
|
||||
* @param amount The number of chips to add to the player.
|
||||
*/
|
||||
public void addChips(int amount) {
|
||||
chips += amount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player's status to the specified value.
|
||||
*
|
||||
* @param status The new status for the player.
|
||||
*/
|
||||
public void setStatus(PlayerStatus status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current status of the player.
|
||||
*
|
||||
* @return The player's status.
|
||||
*/
|
||||
public PlayerStatus getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the player's current hand of cards.
|
||||
*
|
||||
* @return A list of Card objects representing the player's hand.
|
||||
*/
|
||||
public List<Card> getHand() {
|
||||
return hand;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a card to the player's hand.
|
||||
*
|
||||
* @param card The Card object to be added to the player's hand.
|
||||
*/
|
||||
public void giveCard(Card card) {
|
||||
hand.add(card);
|
||||
}
|
||||
|
||||
/** Clears the player's hand of cards, removing all cards from the hand. */
|
||||
public void clearHand() {
|
||||
hand.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has folded in the current round.
|
||||
*
|
||||
* @return true if the player has folded, false otherwise.
|
||||
*/
|
||||
public boolean isFolded() {
|
||||
return folded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the player's folded status to the specified value.
|
||||
*
|
||||
* @param folded The new folded status for the player.
|
||||
*/
|
||||
public void setFolded(boolean folded) {
|
||||
this.folded = folded;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* PlayerId is a value object that represents the unique identifier of a player in the game. It
|
||||
* encapsulates a string value and provides validation to ensure that it is not null. The PlayerId
|
||||
* class also includes a factory method for creating instances and overrides the toString method for
|
||||
* easy representation.
|
||||
*/
|
||||
public record PlayerId(String value) {
|
||||
|
||||
/**
|
||||
* Constructs a PlayerId with the specified value. The constructor validates that the value is
|
||||
* not null.
|
||||
*
|
||||
* @param value the string value representing the player's unique identifier
|
||||
* @throws NullPointerException if the value is null
|
||||
*/
|
||||
public PlayerId {
|
||||
Objects.requireNonNull(value, "PlayerId cannot be null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory method to create a PlayerId instance from a string value.
|
||||
*
|
||||
* @param value the string value representing the player's unique identifier
|
||||
* @return a new PlayerId instance with the specified value
|
||||
*/
|
||||
public static PlayerId of(String value) {
|
||||
return new PlayerId(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the string representation of the PlayerId, which is the encapsulated value.
|
||||
*
|
||||
* @return the string value of the PlayerId
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares this PlayerId with another object for equality.
|
||||
*
|
||||
* @param o the object to compare to
|
||||
* @return true if this PlayerId is equal to the given object, false otherwise
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
PlayerId playerId = (PlayerId) o;
|
||||
return Objects.equals(value, playerId.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the hash code for this PlayerId.
|
||||
*
|
||||
* @return the hash code of the PlayerId
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(value);
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player;
|
||||
|
||||
/**
|
||||
* The PlayerStatus enumeration represents the various states a player can be in during a poker
|
||||
* game. It helps to track the player's current status, such as whether they are actively
|
||||
* participating in the hand, have folded, are all-in, or have been eliminated from the game.
|
||||
*/
|
||||
public enum PlayerStatus {
|
||||
ACTIVE,
|
||||
FOLDED,
|
||||
ALL_IN,
|
||||
OUT
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
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);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
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<Rule> 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<Rule> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
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<Player> playerList = new ArrayList<>(state.getPlayers());
|
||||
Player current = playerList.get(state.getCurrentPlayerIndex());
|
||||
|
||||
if (!current.getId().equals(actingPlayerId)) {
|
||||
throw new RuleViolationException("Not your turn");
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+38
@@ -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.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+40
@@ -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.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+43
@@ -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.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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
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<Player> players = new ArrayList<>(state.getPlayers());
|
||||
|
||||
Player bestPlayer = null;
|
||||
HandRank bestRank = null;
|
||||
|
||||
for (Player player : players) {
|
||||
|
||||
if (player.getStatus() == PlayerStatus.FOLDED) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Card> 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();
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents the betting state of the current hand, including the pot size, the current highest
|
||||
* bet, and the individual bets made by each player. It keeps track of the total pot, the current
|
||||
* bet amount, and the bets made by each player. This class is essential for managing the betting
|
||||
* rounds and ensuring that all players' bets are accounted for during the game.
|
||||
*/
|
||||
public class BettingState {
|
||||
|
||||
private int pot;
|
||||
private int currentBet;
|
||||
|
||||
private Map<String, Integer> playerBets = new HashMap<>();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
|
||||
|
||||
/**
|
||||
* The GamePhase enumeration represents the different phases of a poker game. Each phase corresponds
|
||||
* to a specific stage in the game, such as waiting for players, dealing cards, and determining the
|
||||
* winner. This enumeration is essential for managing the flow of the game and ensuring that actions
|
||||
* are performed at the appropriate times.
|
||||
*/
|
||||
public enum GamePhase {
|
||||
WAITING,
|
||||
PREFLOP,
|
||||
FLOP,
|
||||
TURN,
|
||||
RIVER,
|
||||
SHOWDOWN,
|
||||
FINISHED
|
||||
}
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
|
||||
|
||||
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.player.Player;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.domain.game.player.PlayerId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The GameState class encapsulates the entire state of a poker game at any given moment. It
|
||||
* maintains information about the players, the pot, the current phase of the game, the dealer
|
||||
* position, and the cards in play. This class is central to managing the flow of the game and
|
||||
* ensuring that all actions and decisions are based on an accurate representation of the current
|
||||
* game state.
|
||||
*/
|
||||
public class GameState {
|
||||
|
||||
private Map<PlayerId, Player> players = new HashMap<>();
|
||||
|
||||
private List<PlayerId> playerOrder = new ArrayList<>();
|
||||
|
||||
private Pot pot = new Pot();
|
||||
|
||||
private TableState tableState = new TableState();
|
||||
|
||||
private int currentPlayerIndex;
|
||||
|
||||
private boolean handActive;
|
||||
|
||||
private boolean allowOutOfTurn;
|
||||
|
||||
private GamePhase phase;
|
||||
|
||||
private int dealerIndex;
|
||||
|
||||
private Map<PlayerId, Integer> currentBets = new HashMap<>();
|
||||
|
||||
private Map<PlayerId, Integer> playerBetCommitments = new HashMap<>();
|
||||
|
||||
private Map<PlayerId, List<Card>> holeCards = new HashMap<>();
|
||||
|
||||
private List<Card> communityCards = new ArrayList<>();
|
||||
|
||||
// private final Set<PlayerId> foldedPlayers = new HashSet<>();
|
||||
|
||||
private Deck deck;
|
||||
|
||||
// Getter
|
||||
|
||||
/**
|
||||
* Returns the list of players currently in the game.
|
||||
*
|
||||
* @return A list of Player objects representing the players in the game.
|
||||
*/
|
||||
public Collection<Player> getPlayers() {
|
||||
return players.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current player whose turn it is to act.
|
||||
*
|
||||
* @return The Player object representing the current player.
|
||||
*/
|
||||
public Player getCurrentPlayer() {
|
||||
PlayerId id = playerOrder.get(currentPlayerIndex);
|
||||
return players.get(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the current player in the players list.
|
||||
*
|
||||
* @return An integer representing the index of the current player.
|
||||
*/
|
||||
public int getCurrentPlayerIndex() {
|
||||
return currentPlayerIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether a hand is currently active in the game.
|
||||
*
|
||||
* @return true if a hand is active, false otherwise.
|
||||
*/
|
||||
public boolean isHandActive() {
|
||||
return handActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current phase of the game (e.g., PREFLOP, FLOP, TURN, RIVER).
|
||||
*
|
||||
* @return The GamePhase enum value representing the current phase of the game.
|
||||
*/
|
||||
public GamePhase getPhase() {
|
||||
return phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current state of the table, including player statuses and positions.
|
||||
*
|
||||
* @return A TableState object representing the current state of the table.
|
||||
*/
|
||||
public TableState getTableState() {
|
||||
return tableState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current pot, which contains the total amount of chips bet by players in the
|
||||
* current hand.
|
||||
*
|
||||
* @return A Pot object representing the current pot.
|
||||
*/
|
||||
public Pot getPot() {
|
||||
return pot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current deck of cards being used in the game.
|
||||
*
|
||||
* @return A Deck object representing the current deck of cards.
|
||||
*/
|
||||
public Deck getDeck() {
|
||||
return deck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of community cards currently on the table.
|
||||
*
|
||||
* @return A list of Card objects representing the community cards.
|
||||
*/
|
||||
public List<Card> getCommunityCards() {
|
||||
return communityCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return A list of Card objects representing the player's hole cards, or an empty list if the
|
||||
* player has no hole cards.
|
||||
*/
|
||||
public List<Card> getHoleCards(PlayerId playerId) {
|
||||
return holeCards.getOrDefault(playerId, new ArrayList<>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the dealer in the players list.
|
||||
*
|
||||
* @return An integer representing the index of the dealer.
|
||||
*/
|
||||
public int getDealerIndex() {
|
||||
return dealerIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Card objects
|
||||
* representing the player's hole cards.
|
||||
*/
|
||||
public Map<PlayerId, List<Card>> getPlayerCards() {
|
||||
return holeCards;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of players currently in the game.
|
||||
*
|
||||
* @return An integer representing the number of players in the game.
|
||||
*/
|
||||
public int getPlayerCount() {
|
||||
return players.size();
|
||||
}
|
||||
|
||||
// SETTER
|
||||
|
||||
/**
|
||||
* Sets the index of the current player in the players list.
|
||||
*
|
||||
* @param index An integer representing the index of the current player.
|
||||
*/
|
||||
public void setCurrentPlayerIndex(int index) {
|
||||
this.currentPlayerIndex = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether a hand is currently active in the game.
|
||||
*
|
||||
* @param handActive A boolean value indicating whether a hand is active.
|
||||
*/
|
||||
public void setHandActive(boolean handActive) {
|
||||
this.handActive = handActive;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current phase of the game.
|
||||
*
|
||||
* @param phase The GamePhase enum value representing the new phase of the game.
|
||||
*/
|
||||
public void setPhase(GamePhase phase) {
|
||||
this.phase = phase;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the index of the dealer in the players list.
|
||||
*
|
||||
* @param dealerIndex An integer representing the index of the dealer.
|
||||
*/
|
||||
public void setDealerIndex(int dealerIndex) {
|
||||
this.dealerIndex = dealerIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 the game.
|
||||
*/
|
||||
public void setDeck(Deck deck) {
|
||||
this.deck = deck;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to the game with the specified ID and initial chip count. This method creates a
|
||||
* new Player object, adds it to the list of players, and initializes the player's current bet
|
||||
* and bet commitment in the game state.
|
||||
*
|
||||
* @param id The ID of the player to add.
|
||||
* @param chips The initial number of chips the player has.
|
||||
*/
|
||||
public void addPlayer(PlayerId id, int chips) {
|
||||
|
||||
Player player = new Player(id, chips);
|
||||
|
||||
players.put(id, player);
|
||||
playerOrder.add(id);
|
||||
|
||||
currentBets.put(id, 0);
|
||||
playerBetCommitments.put(id, 0);
|
||||
}
|
||||
|
||||
// BETTING LOGIC
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return An integer representing the current bet amount for the specified player, or 0 if the
|
||||
* player has not placed any bets.
|
||||
*/
|
||||
public int getCurrentBet(PlayerId playerId) {
|
||||
return currentBets.getOrDefault(playerId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current bet amount for a specific player based on their ID. This method updates the
|
||||
* currentBets map with the new bet amount for the specified player.
|
||||
*
|
||||
* @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.
|
||||
*/
|
||||
public void setCurrentBet(PlayerId playerId, int amount) {
|
||||
currentBets.put(playerId, amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the current bets for all players by clearing the currentBets map. This method is
|
||||
* typically called at the start of a new hand to ensure that all players' bets are reset to
|
||||
* zero.
|
||||
*/
|
||||
public void resetBets() {
|
||||
currentBets.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a specified amount to the pot. This method updates the total amount in the pot by adding
|
||||
* the given amount to it.
|
||||
*
|
||||
* @param amount The amount of chips to be added to the pot.
|
||||
*/
|
||||
public void addToPot(int amount) {
|
||||
pot.add(amount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether out-of-turn actions are allowed in the game. Out-of-turn actions refer to
|
||||
* players being able to act when it is not their turn, which can be a feature in some poker
|
||||
* variants or game modes.
|
||||
*
|
||||
* @return true if out-of-turn actions are allowed, false otherwise.
|
||||
*/
|
||||
public boolean isAllowOutOfTurn() {
|
||||
return allowOutOfTurn;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether out-of-turn actions are allowed in the game. This method updates the
|
||||
* allowOutOfTurn flag, which determines if players can act when it is not their turn.
|
||||
*
|
||||
* @param allowOutOfTurn A boolean value indicating whether out-of-turn actions should be
|
||||
* allowed in the game.
|
||||
*/
|
||||
public void setAllowOutOfTurn(boolean allowOutOfTurn) {
|
||||
this.allowOutOfTurn = allowOutOfTurn;
|
||||
}
|
||||
|
||||
// PLAYER HELPERS
|
||||
|
||||
/**
|
||||
* Retrieves a player from the game based on their ID. This method searches the list of players
|
||||
* for a player with the specified ID and returns it. If no player with the given ID is found, a
|
||||
* RuntimeException is thrown.
|
||||
*
|
||||
* @param id The ID of the player to retrieve.
|
||||
* @return The Player object corresponding to the specified ID.
|
||||
* @throws RuntimeException if no player with the given ID is found in the game.
|
||||
*/
|
||||
public Player getPlayer(PlayerId id) {
|
||||
Player player = players.get(id);
|
||||
|
||||
if (player == null) {
|
||||
throw new RuntimeException("Player not found: " + id);
|
||||
}
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
// BET COMMITMENTS
|
||||
|
||||
/**
|
||||
* Retrieves the current bet commitment for a specific player based on their ID. A bet
|
||||
* commitment represents the total amount a player has committed to the pot in the current hand,
|
||||
* including all bets, raises, and calls they have made.
|
||||
*
|
||||
* @param playerId The ID of the player whose current bet commitment is being requested.
|
||||
* @return An integer representing the current bet commitment for the specified player, or 0 if
|
||||
* the player has not made any bet commitments.
|
||||
*/
|
||||
public int getCurrentBetCommitment(PlayerId playerId) {
|
||||
return playerBetCommitments.getOrDefault(playerId, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current bet commitment for a specific player based on their ID. This method updates
|
||||
* the playerBetCommitments map with the new bet commitment amount for the specified player.
|
||||
*
|
||||
* @param playerId The ID of the player whose current bet commitment is being set.
|
||||
* @param amount The new bet commitment amount to be set for the specified player.
|
||||
*/
|
||||
public void setCurrentBetCommitment(PlayerId playerId, int amount) {
|
||||
playerBetCommitments.put(playerId, amount);
|
||||
}
|
||||
|
||||
// CARDS
|
||||
|
||||
/**
|
||||
* Gives hole cards to a specific player based on their ID. This method takes two Card objects
|
||||
* representing the player's hole cards and adds them to the holeCards map under the player's
|
||||
* ID.
|
||||
*
|
||||
* @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 cards.
|
||||
* @param c2 The second Card object representing the other hole card for the player.
|
||||
*/
|
||||
public void giveHoleCards(PlayerId playerId, Card c1, Card c2) {
|
||||
|
||||
List<Card> cards = new ArrayList<>();
|
||||
cards.add(c1);
|
||||
cards.add(c2);
|
||||
|
||||
holeCards.put(playerId, cards);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a community card to the game state. This method takes a Card object representing a
|
||||
* community card and adds it to the list of community cards on the table.
|
||||
*
|
||||
* @param card The Card object representing the community card to be added to the game state.
|
||||
*/
|
||||
public void addCommunityCard(Card card) {
|
||||
communityCards.add(card);
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the community cards by clearing the list of community cards. This method is typically
|
||||
* called at the start of a new hand to ensure that all community cards from the previous hand
|
||||
* are removed from the game state.
|
||||
*/
|
||||
public void resetCommunityCards() {
|
||||
communityCards.clear();
|
||||
}
|
||||
|
||||
// TURN MANAGEMENT
|
||||
|
||||
/**
|
||||
* Advances the turn to the next player in the players list. This method updates the
|
||||
* currentPlayerIndex by incrementing it and wrapping around to the start of the list if
|
||||
* necessary, ensuring that the turn order is maintained correctly throughout the game.
|
||||
*/
|
||||
public void nextPlayer() {
|
||||
int start = currentPlayerIndex;
|
||||
|
||||
do {
|
||||
currentPlayerIndex = (currentPlayerIndex + 1) % playerOrder.size();
|
||||
|
||||
Player p = getCurrentPlayer();
|
||||
|
||||
if (!p.isFolded() && !p.isAllIn()) {
|
||||
return;
|
||||
}
|
||||
|
||||
} while (currentPlayerIndex != start);
|
||||
}
|
||||
|
||||
// Dealer Rotation
|
||||
|
||||
/**
|
||||
* Rotates the dealer position to the next player in the players list. This method updates the
|
||||
* dealerIndex by incrementing it and wrapping around to the start of the list if necessary,
|
||||
* ensuring that the dealer position rotates correctly after each hand.
|
||||
*/
|
||||
public void rotateDealer() {
|
||||
dealerIndex = (dealerIndex + 1) % playerOrder.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current dealer based on the dealerIndex. This method returns the Player object
|
||||
* corresponding to the current dealer position in the players list.
|
||||
*
|
||||
* @return The Player object representing the current dealer.
|
||||
*/
|
||||
public Player getDealer() {
|
||||
PlayerId id = playerOrder.get(dealerIndex);
|
||||
return players.get(id);
|
||||
}
|
||||
|
||||
// HAND RESET
|
||||
|
||||
/**
|
||||
* Starts a new hand by resetting the game state for the next round of poker. This method sets
|
||||
* the handActive flag to true, resets the game phase to PREFLOP, clears the pot, resets all
|
||||
* player bets and bet commitments, clears the community cards, and initializes a new shuffled
|
||||
* deck of cards for the new hand.
|
||||
*/
|
||||
public void startNewHand() {
|
||||
|
||||
handActive = true;
|
||||
phase = GamePhase.PREFLOP;
|
||||
|
||||
pot = new Pot();
|
||||
|
||||
resetBets();
|
||||
playerBetCommitments.clear();
|
||||
|
||||
resetCommunityCards();
|
||||
holeCards.clear();
|
||||
|
||||
for (Player player : players.values()) {
|
||||
player.setFolded(false);
|
||||
}
|
||||
|
||||
deck = new Deck();
|
||||
deck.shuffle();
|
||||
|
||||
currentPlayerIndex = (dealerIndex + 1) % playerOrder.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Folds a player in the current hand. This method adds the specified player's ID to the set of
|
||||
* folded players, indicating that the player has folded and is no longer active in the current
|
||||
* hand.
|
||||
*
|
||||
* @param playerId The ID of the player who is folding.
|
||||
*/
|
||||
public void foldPlayer(PlayerId playerId) {
|
||||
getPlayer(playerId).setFolded(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific player has folded in the current hand. This method checks if the
|
||||
* specified player's ID is present in the set of folded players, indicating that the player has
|
||||
* folded.
|
||||
*
|
||||
* @param playerId The ID of the player to check for folding status.
|
||||
* @return true if the player has folded, false otherwise.
|
||||
*/
|
||||
public boolean isFolded(PlayerId playerId) {
|
||||
return getPlayer(playerId).isFolded();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
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 poker game. It
|
||||
* provides methods to add chips to the pot, retrieve the current amount, and reset the pot for a
|
||||
* new round. This class is essential for managing the betting aspect of the game and ensuring that
|
||||
* the pot is accurately maintained throughout the game.
|
||||
*/
|
||||
public class Pot {
|
||||
|
||||
// Disabled because it caused the port to be set to 0
|
||||
// private int total;
|
||||
private int amount = 0;
|
||||
|
||||
/**
|
||||
* Adds the specified amount of chips to the pot.
|
||||
*
|
||||
* @param chips The number of chips to add to the pot.
|
||||
*/
|
||||
public void add(int chips) {
|
||||
amount += chips;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the current amount of chips in the pot.
|
||||
*
|
||||
* @return The current amount of chips in the pot.
|
||||
*/
|
||||
public int getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
// Disabled because it caused the port to be set to 0
|
||||
// public int getTotal() {
|
||||
// return total;
|
||||
// }
|
||||
|
||||
/** Resets the pot to zero, typically used at the end of a round or when starting a new game. */
|
||||
public void reset() {
|
||||
amount = 0;
|
||||
}
|
||||
}
|
||||
+150
@@ -0,0 +1,150 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.domain.game.state;
|
||||
|
||||
/**
|
||||
* The TableState class represents the current state of the poker table during a game. It keeps
|
||||
* track of the current bet, the last raise size, the minimum raise amount, and the big blind.
|
||||
* Additionally, it manages the betting status, including whether betting is currently open and if
|
||||
* it can be reopened after being closed. The class also tracks the last aggressor's ID to determine
|
||||
* who made the most recent aggressive action (like a raise) in the betting round.
|
||||
*/
|
||||
public class TableState {
|
||||
|
||||
private int currentBet;
|
||||
private int lastRaiseSize;
|
||||
private int minRaise;
|
||||
private int bigBlind;
|
||||
|
||||
private boolean bettingOpen;
|
||||
private boolean canReopenBetting;
|
||||
|
||||
private String lastAggressorId;
|
||||
|
||||
/**
|
||||
* Retrieves the current bet amount for the table. This value represents the highest bet that
|
||||
* has been placed in the current betting round and is used to determine how much players need
|
||||
* to call or raise to stay in the hand.
|
||||
*
|
||||
* @return The current bet amount for the table.
|
||||
*/
|
||||
public int getCurrentBet() {
|
||||
return currentBet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current bet amount for the table. This method is typically called when a player
|
||||
* places a bet or raises, updating the current bet to reflect the new amount.
|
||||
*
|
||||
* @param currentBet The new current bet amount to set for the table.
|
||||
*/
|
||||
public void setCurrentBet(int currentBet) {
|
||||
this.currentBet = currentBet;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the big blind amount. The big blind serves as a baseline for betting and is often
|
||||
* used to determine the minimum bet or raise.
|
||||
*
|
||||
* @return The big blind amount.
|
||||
*/
|
||||
public int getBigBlind() {
|
||||
return bigBlind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the big blind amount for the game. The big blind is a forced bet that players must post
|
||||
* before the cards are dealt, and it serves as a baseline for betting in the game.
|
||||
*
|
||||
* @param bigBlind The amount to set as the big blind for the game.
|
||||
*/
|
||||
public void setBigBlind(int bigBlind) {
|
||||
this.bigBlind = bigBlind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the minimum raise amount for the current betting round. The minimum raise is
|
||||
* typically determined by the size of the last raise or the big blind, ensuring that players
|
||||
* must raise by at least a certain amount.
|
||||
*
|
||||
* @return The minimum raise amount for the current betting round.
|
||||
*/
|
||||
public int getMinRaise() {
|
||||
return minRaise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the minimum raise amount for the current betting round. This method is typically called
|
||||
* after a raise is made, updating the minimum raise to ensure that subsequent raises meet the
|
||||
* required amount.
|
||||
*
|
||||
* @param minRaise The new minimum raise amount to set for the current betting round.
|
||||
*/
|
||||
public void setMinRaise(int minRaise) {
|
||||
this.minRaise = minRaise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if betting is currently open at the table. This status indicates whether players are
|
||||
* allowed to place bets or raises during the current betting round.
|
||||
*
|
||||
* @return true if betting is open, false otherwise.
|
||||
*/
|
||||
public boolean isBettingOpen() {
|
||||
return bettingOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the betting status for the table. This method can be used to open or close betting
|
||||
* during a betting round, controlling whether players can place bets or raises.
|
||||
*
|
||||
* @param bettingOpen The new betting status to set for the table (true for open, false for
|
||||
* closed).
|
||||
*/
|
||||
public void setBettingOpen(boolean bettingOpen) {
|
||||
this.bettingOpen = bettingOpen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if betting can be reopened after being closed. This status allows for scenarios where
|
||||
* betting may be temporarily closed (e.g., after a raise) but can be reopened to allow other
|
||||
* players to respond.
|
||||
*
|
||||
* @return true if betting can be reopened, false otherwise.
|
||||
*/
|
||||
public boolean canReopenBetting() {
|
||||
return canReopenBetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether betting can be reopened after being closed. This method is typically called
|
||||
* after a raise is made, allowing for the possibility of reopening betting to let other players
|
||||
* respond to the raise.
|
||||
*
|
||||
* @param canReopenBetting The new status indicating whether betting can be reopened (true or
|
||||
* false).
|
||||
*/
|
||||
public void setCanReopenBetting(boolean canReopenBetting) {
|
||||
this.canReopenBetting = canReopenBetting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the ID of the last aggressor in the current betting round. The last aggressor is
|
||||
* the player who made the most recent aggressive action (like a raise) and is important for
|
||||
* determining the flow of betting and who may have the opportunity to respond.
|
||||
*
|
||||
* @return The ID of the last aggressor in the current betting round.
|
||||
*/
|
||||
public String getLastAggressorId() {
|
||||
return lastAggressorId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the ID of the last aggressor in the current betting round. This method is typically
|
||||
* called after a player makes an aggressive action (like a raise), updating the last aggressor
|
||||
* ID to reflect the most recent aggressive action.
|
||||
*
|
||||
* @param lastAggressorId The new ID of the last aggressor to set for the current betting round.
|
||||
*/
|
||||
public void setLastAggressorId(String lastAggressorId) {
|
||||
this.lastAggressorId = lastAggressorId;
|
||||
}
|
||||
}
|
||||
@@ -86,15 +86,38 @@ public class UserRegistry {
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a user by their session ID.
|
||||
* Looks up a user by their {@link SessionId}.
|
||||
*
|
||||
* @param sessionId the session ID to look up
|
||||
* @return an Optional containing the user, or empty if no user is associated with this session
|
||||
* @param sessionId the SessionId to look up
|
||||
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||
* SessionId
|
||||
*/
|
||||
public Optional<User> findBySessionId(SessionId sessionId) {
|
||||
public Optional<User> getBySessionId(SessionId sessionId) {
|
||||
return Optional.ofNullable(bySessionId.get(sessionId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a user by their {@link UserId}.
|
||||
*
|
||||
* @param userId the UserId to look up
|
||||
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||
* UserId
|
||||
*/
|
||||
public Optional<User> getByUserId(UserId userId) {
|
||||
return Optional.ofNullable(byId.get(userId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up a user by their username.
|
||||
*
|
||||
* @param username the username to look up
|
||||
* @return an Optional containing the {@link User}, or empty if no user is associated with this
|
||||
* username
|
||||
*/
|
||||
public Optional<User> getByUsername(String username) {
|
||||
return Optional.ofNullable(byName.get(username));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all currently registered users.
|
||||
*
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher;
|
||||
|
||||
public class ResponseDispatchException extends RuntimeException {
|
||||
public ResponseDispatchException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
+9
-4
@@ -27,12 +27,17 @@ public class ResponseDispatcher {
|
||||
* the target session's response queue.
|
||||
*
|
||||
* @param response the response to dispatch
|
||||
* @throws InterruptedException if the thread is interrupted while waiting to enqueue the
|
||||
* primitive response
|
||||
* @throws ResponseDispatchException wraps any exceptions that occur during dispatching, such as
|
||||
* the {@link InterruptedException}
|
||||
*/
|
||||
public void dispatch(Response response) throws InterruptedException {
|
||||
public void dispatch(Response response) {
|
||||
PrimitiveResponse primitiveResponse = ResponseEncoder.encode(response);
|
||||
Session session = sessionManager.getSessionById(response.getSessionId());
|
||||
session.getResponseQueue().put(primitiveResponse);
|
||||
try {
|
||||
session.getResponseQueue().put(primitiveResponse);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
throw new ResponseDispatchException("Interrupted while dispatching response", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -12,8 +12,10 @@ import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Primitive
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RawRequest;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.Request;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.RequestContext;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.request.accessor.MissingParameterException;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.ErrorResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.PrimitiveResponse;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseDispatchException;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.protocol.response.dispatcher.ResponseEncoder;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.RawPacket;
|
||||
import ch.unibas.dmi.dbis.cs108.casono.server.network.transport.TransportLayer;
|
||||
@@ -88,6 +90,19 @@ public class SessionReader implements Runnable {
|
||||
"UNKNOWN_COMMAND",
|
||||
"This command is unknown to the server."));
|
||||
|
||||
} catch (ResponseDispatchException e) {
|
||||
logger.error(
|
||||
"Unexpected ResponseDispatchException exception while dispatching request",
|
||||
e);
|
||||
|
||||
} catch (MissingParameterException e) {
|
||||
logger.error(
|
||||
"Recieved request for command '{}' was missing the '{}' parameter",
|
||||
rawRequest.command(),
|
||||
e.getParameterKey());
|
||||
sendErrorResponse(
|
||||
new ErrorResponse(requestContext, "MISSING_PARAMETER", e.getMessage()));
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.error("Unexpected IO exception while reading from transport", e);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user