From ce583062ba8c3309ddabd4be90f5d116ac1fc0c4 Mon Sep 17 00:00:00 2001 From: Julian Kropff Date: Fri, 10 Apr 2026 20:16:08 +0200 Subject: [PATCH] Refactor: improve GameClient structure Refs #52 --- .../casono/client/network/GameClient.java | 224 ++++++++++++------ 1 file changed, 150 insertions(+), 74 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java index 0ae8225..ad77ae8 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/client/network/GameClient.java @@ -3,123 +3,199 @@ package ch.unibas.dmi.dbis.cs108.casono.client.network; import ch.unibas.dmi.dbis.cs108.casono.client.game.Card; import ch.unibas.dmi.dbis.cs108.casono.client.game.GameState; import ch.unibas.dmi.dbis.cs108.casono.client.game.Player; - -import java.util.ArrayList; -import java.util.List; +import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerId; +import ch.unibas.dmi.dbis.cs108.casono.client.game.PlayerState; /** - * The GameClient class is responsible for communicating with the server to - * retrieve the current game state. It sends a command to the server and - * parses the response into a structured GameState object. + * The GameClient class is responsible for communicating with the server to retrieve the current + * game state. It sends a command to the server and parses the response into a structured GameState + * object. */ public class GameClient { private final ClientService client; + private final int gameId; /** * Constructs a GameClient with the given ClientService for communication. * - * @param client The ClientService instance used to send commands and receive - * responses from the server. + * @param client The ClientService instance used to send commands and receive responses from the + * server. */ - public GameClient(ClientService client) { + public GameClient(ClientService client, int gameId) { this.client = client; + this.gameId = gameId; } /** - * Retrieves the current game state from the server by sending a command and - * parsing the response. + * Fetch the current game state from the server by sending a "GET_GAME_STATE" * - * @return A GameState object representing the current state of the game. + * @return A GameState object representing the current state of the game, as parsed */ - public GameState getGameState() { - String response = client.processCommand("GET_GAME_STATE"); - return parseGameState(response); + public GameState fetchGameState() { + + String response = client.processCommand("GET_GAME_STATE\nGAME_ID=" + gameId); + + if (response == null || response.isBlank()) { + throw new RuntimeException("Empty server response"); + } + + return parse(response); + } + + /** Send a CALL command to the server to indicate that the player wants to call */ + public void sendCall() { + client.processCommand("CALL\nGAME_ID=" + gameId); + } + + /** Send a FOLD command to the server to indicate that the player wants to fold */ + public void sendFold() { + client.processCommand("FOLD\nGAME_ID=" + gameId); + } + + /** Send a BET command to the server to indicate that the player wants to bet */ + public void sendBet(int amount) { + client.processCommand("BET\nGAME_ID=" + gameId + "\nAMOUNT=" + amount); } /** - * Parses the raw response from the server into a structured GameState object. + * Send a RAISE command to the server to indicate that the player wants to raise * - * @param input The raw response string from the server. - * @return A GameState object representing the current state of the game. + * @param amount The amount to raise to */ - private GameState parseGameState(String input) { + public void sendRaise(int amount) { + client.processCommand("RAISE\nGAME_ID=" + gameId + "\nAMOUNT=" + amount); + } + + /** + * Parse the raw string response from the server into a structured GameState object. + * + * @param input The raw string response from the server representing the game state. + * @return A GameState object representing the parsed game state. + */ + private GameState parse(String input) { GameState state = new GameState(); - String[] lines = input.split("\n"); - Player currentPlayer = null; Card currentCard = null; - for (String rawLine : lines) { + boolean inPlayers = false; + boolean inPlayerCards = false; + boolean inCommunityCards = false; - String line = rawLine.trim(); + for (String raw : input.split("\n")) { - if (line.startsWith("+OK") || line.equals("END")) { + String line = raw.trim(); + + if (line.isEmpty() || line.startsWith("+OK")) { continue; } if (line.startsWith("PHASE=")) { - state.phase = line.split("=")[1]; - } - - else if (line.startsWith("POT=")) { - state.pot = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("CURRENT_BET=")) { - state.currentBet = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("DEALER=")) { - state.dealer = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("ACTIVE_PLAYER=")) { - state.activePlayer = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("PLAYER")) { + state.phase = value(line); + } else if (line.startsWith("POT=")) { + state.pot = intVal(line); + } else if (line.startsWith("CURRENT_BET=")) { + state.currentBet = intVal(line); + } else if (line.startsWith("DEALER=")) { + state.dealer = intVal(line); + } else if (line.startsWith("ACTIVE_PLAYER=")) { + state.activePlayer = intVal(line); + } else if (line.startsWith("WINNER=")) { + state.winnerIndex = intVal(line); + } else if (line.equals("END")) { + inPlayers = false; + inPlayerCards = false; + inCommunityCards = false; + currentPlayer = null; + currentCard = null; + continue; + } else if (line.equals("PLAYERS")) { + inPlayers = true; + inPlayerCards = false; + inCommunityCards = false; + continue; + } else if (line.equals("PLAYER")) { currentPlayer = new Player(); state.players.add(currentPlayer); - } + continue; + } else if (line.startsWith("NAME=") && currentPlayer != null) { + currentPlayer.setId(PlayerId.of(value(line))); + } else if (line.startsWith("CHIPS=") && currentPlayer != null) { + currentPlayer.setChips(intVal(line)); + } else if (line.startsWith("BET=") && currentPlayer != null) { + currentPlayer.setBet(intVal(line)); + } else if (line.startsWith("STATE=") && currentPlayer != null) { + currentPlayer.setState(parseState(value(line))); + } else if (line.equals("CARDS")) { - else if (line.startsWith("NAME=") && currentPlayer != null) { - currentPlayer.name = line.split("=")[1]; - } - - else if (line.startsWith("CHIPS=") && currentPlayer != null) { - currentPlayer.chips = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("BET=") && currentPlayer != null) { - currentPlayer.bet = Integer.parseInt(line.split("=")[1]); - } - - else if (line.startsWith("STATE=") && currentPlayer != null) { - currentPlayer.state = line.split("=")[1]; - } - - else if (line.startsWith("CARD")) { - currentCard = new Card(); - - if (currentPlayer != null) { - currentPlayer.cards.add(currentCard); + if (inPlayers && currentPlayer != null) { + inPlayerCards = true; + inCommunityCards = false; } else { + inCommunityCards = true; + inPlayerCards = false; + } + + continue; + } else if (line.startsWith("CARD")) { + currentCard = new Card("", ""); + + if (inPlayerCards && currentPlayer != null) { + currentPlayer.addCard(currentCard); + } else if (inCommunityCards) { state.communityCards.add(currentCard); } - } - - else if (line.startsWith("VALUE=") && currentCard != null) { - currentCard.value = line.split("=")[1]; - } - - else if (line.startsWith("SUIT=") && currentCard != null) { - currentCard.suit = line.split("=")[1]; + } else if (line.startsWith("VALUE=") && currentCard != null) { + currentCard.setValue(value(line)); + } else if (line.startsWith("SUIT=") && currentCard != null) { + currentCard.setSuit(value(line)); } } return state; } + + /** + * Helper method to extract the value from a line in the format KEY=VALUE. + * + * @param line The input line from which to extract the value, expected to be in the format + * KEY=VALUE. + * @return The extracted value part of the input line, which is the substring after the first + * '=' character. + */ + private String value(String line) { + return line.split("=", 2)[1]; + } + + /** + * Helper method to extract an integer value from a line in the format KEY=VALUE. + * + * @param line The input line from which to extract the integer value, expected to be in the + * format KEY=VALUE where VALUE is an integer. + * @return The extracted integer value from the input line, parsed from the substring after the + * first '=' character. + */ + private int intVal(String line) { + return Integer.parseInt(value(line)); + } + + /** + * Helper method to parse a string representation of a player's state into the corresponding + * PlayerState enum value. + * + * @param s The input string representing the player's state, expected to be one of ACTIVE, + * FOLDED, or DEALER (case-insensitive). + * @return The corresponding PlayerState enum value based on the input string. If the input does + * not match any known state, it defaults to PlayerState.ACTIVE. + */ + private PlayerState parseState(String s) { + return switch (s.toUpperCase()) { + case "ACTIVE" -> PlayerState.ACTIVE; + case "FOLDED" -> PlayerState.FOLDED; + case "DEALER" -> PlayerState.DEALER; + default -> PlayerState.ACTIVE; + }; + } }