From 34bac0adb62c7aa294c1e2f778468bb7248136c2 Mon Sep 17 00:00:00 2001 From: Jona Walpert Date: Tue, 12 May 2026 16:00:38 +0200 Subject: [PATCH 1/2] Feat: Money is now saved in the Higshcore list. Furthermore if two people win the pot both will get added to the highscore list --- .../get_game_state/GetGameStateResponse.java | 16 +++-- .../domain/highscore/HighscoreService.java | 60 ++++++++++++++++++- .../resources/ui-structure/Casinomainui.fxml | 2 +- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java index d83288a..7b6e33d 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/app/commands/game/get_game_state/GetGameStateResponse.java @@ -60,12 +60,9 @@ public class GetGameStateResponse extends SuccessResponse { builder.param("WINNER", winnerIndex); List winnerNames = computeWinnerNames(state, game); - int potPerWinner = computePotPerWinner(state, winnerNames.size()); - for (String name : winnerNames) { builder.param("WINNER_NAME", name); } - builder.param("POT_PER_WINNER", potPerWinner); appendWinnerToHighscoresIfFinished(state, winnerNames); appendHighscoreEntries(builder); @@ -92,13 +89,22 @@ public class GetGameStateResponse extends SuccessResponse { return; } - // Keep ordering stable, avoid duplicate writes for the same winner in one hand. + // Keep ordering stable and persist tied winners as a single shared highscore + // entry. Compute the per-winner share here (pot is split equally among + // winners) + int potPerWinner = computePotPerWinner(state, winnerNames.size()); Set uniqueWinners = new LinkedHashSet<>(winnerNames); + List formattedWinners = new ArrayList<>(); for (String name : uniqueWinners) { if (name != null && !name.isBlank()) { - HighscoreService.getInstance().appendWinner(name); + formattedWinners.add(name + " (" + Math.max(0, potPerWinner) + ")"); } } + + if (!formattedWinners.isEmpty()) { + HighscoreService.getInstance() + .appendFormattedEntry(String.join(", ", formattedWinners)); + } } private static Player findPlayerByIndex(GameState state, int winnerIndex) { diff --git a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java index 929b3e3..f47f5a5 100644 --- a/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java +++ b/src/main/java/ch/unibas/dmi/dbis/cs108/casono/server/domain/highscore/HighscoreService.java @@ -18,6 +18,7 @@ public final class HighscoreService { private static final DateTimeFormatter DISPLAY_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").withZone(ZoneId.systemDefault()); private static final int MAX_RETURNED_ENTRIES = 100; + private static final int MIN_MONEY_COLUMN_COUNT = 3; private static final String LINE_SEPARATOR = "\t"; private final Path storagePath; @@ -32,6 +33,11 @@ public final class HighscoreService { } public synchronized void appendWinner(String winnerName) { + appendWinner(winnerName, 0); + } + + /** Append a winner with an optional money value (money may be 0). */ + public synchronized void appendWinner(String winnerName, int money) { if (winnerName == null) { return; } @@ -41,6 +47,44 @@ public final class HighscoreService { return; } + String line; + if (money > 0) { + line = + Instant.now() + + LINE_SEPARATOR + + sanitized + + LINE_SEPARATOR + + money + + System.lineSeparator(); + } else { + // Keep legacy two-column format for backward compatibility + line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); + } + + try { + Files.createDirectories(storagePath.getParent()); + Files.writeString( + storagePath, + line, + StandardCharsets.UTF_8, + StandardOpenOption.CREATE, + StandardOpenOption.APPEND); + } catch (IOException ignored) { + // Highscore persistence must never break the game state response path. + } + } + + /** Append a preformatted highscore entry such as "Lars (1000), Jona (1000)". */ + public synchronized void appendFormattedEntry(String entryText) { + if (entryText == null) { + return; + } + + String sanitized = sanitizeName(entryText); + if (sanitized.isEmpty()) { + return; + } + String line = Instant.now() + LINE_SEPARATOR + sanitized + System.lineSeparator(); try { @@ -74,15 +118,25 @@ public final class HighscoreService { if (raw == null || raw.isBlank()) { continue; } - - String[] parts = raw.split(LINE_SEPARATOR, 2); + // Support both legacy format (TIMESTAMP \t NAME) and new format (TIMESTAMP \t + // NAME \t MONEY) + String[] parts = raw.split(LINE_SEPARATOR); if (parts.length < 2) { continue; } try { Instant ts = Instant.parse(parts[0].trim()); - String display = DISPLAY_FORMATTER.format(ts) + " | " + parts[1].trim(); + String name = parts[1].trim(); + String display = DISPLAY_FORMATTER.format(ts) + " | " + name; + if (parts.length >= MIN_MONEY_COLUMN_COUNT) { + try { + int money = Integer.parseInt(parts[2].trim()); + display = display + " | $" + money; + } catch (NumberFormatException nfe) { + // ignore malformed money + } + } formatted.add(display); } catch (Exception ignored) { // Skip malformed lines and keep all valid entries. diff --git a/src/main/resources/ui-structure/Casinomainui.fxml b/src/main/resources/ui-structure/Casinomainui.fxml index b5df10b..0b734ee 100644 --- a/src/main/resources/ui-structure/Casinomainui.fxml +++ b/src/main/resources/ui-structure/Casinomainui.fxml @@ -4,7 +4,7 @@ - Date: Tue, 12 May 2026 16:02:39 +0200 Subject: [PATCH 2/2] Style: Fixed spotless error --- .../TaskbarControllerInputValidationTest.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarControllerInputValidationTest.java b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarControllerInputValidationTest.java index bc2cd31..4751205 100644 --- a/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarControllerInputValidationTest.java +++ b/src/test/java/ch/unibas/dmi/dbis/cs108/casono/client/ui/gameui/gameuicomponents/TaskbarControllerInputValidationTest.java @@ -14,10 +14,10 @@ class TaskbarControllerInputValidationTest { void updateLogicRunsWithoutCrash() { GameState state = new GameState(); - state.players = List.of( - new Player(PlayerId.of("me"), 20000), - new Player(PlayerId.of("other"), 20000) - ); + state.players = + List.of( + new Player(PlayerId.of("me"), 20000), + new Player(PlayerId.of("other"), 20000)); state.phase = "PREFLOP"; state.currentBet = 200; @@ -31,7 +31,6 @@ class TaskbarControllerInputValidationTest { } static class DummyController { - void update(GameState state, PlayerId id) { - } + void update(GameState state, PlayerId id) {} } }