From 134c9a2fd84a5602046f8749f40595ac1bde711c Mon Sep 17 00:00:00 2001 From: michiel Date: Thu, 4 Dec 2025 14:31:04 +0100 Subject: [PATCH] better human/ai selector with bot selection and depth on TicTacToeAIR --- app/src/main/java/org/toop/app/Server.java | 2 +- .../app/widget/complex/PlayerInfoWidget.java | 96 ++++++++++++------- .../app/widget/view/LocalMultiplayerView.java | 4 +- .../localization/localization_ar.properties | 3 + .../localization/localization_de.properties | 3 + .../localization/localization_en.properties | 3 + .../localization/localization_es.properties | 4 +- .../localization/localization_fr.properties | 3 + .../localization/localization_hi.properties | 3 + .../localization/localization_it.properties | 3 + .../localization/localization_ja.properties | 3 + .../localization/localization_ko.properties | 3 + .../localization/localization_nl.properties | 3 + .../localization/localization_ru.properties | 3 + .../localization/localization_zh.properties | 3 + .../org/toop/game/games/reversi/ReversiR.java | 14 +-- .../game/games/tictactoe/TicTacToeAIR.java | 8 +- .../toop/game/tictactoe/TicTacToeAIRTest.java | 2 +- 18 files changed, 113 insertions(+), 50 deletions(-) diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index 7681da0..74cebae 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -199,7 +199,7 @@ public final class Server { switch (type){ case TICTACTOE ->{ - players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(), user); + players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(9), user); } case REVERSI ->{ players[myTurn] = new ArtificialPlayer<>(new ReversiAIR(), user); diff --git a/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java b/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java index 90ec2a5..c69edf4 100644 --- a/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/PlayerInfoWidget.java @@ -5,10 +5,13 @@ import org.toop.app.widget.Primitive; import javafx.scene.Node; import javafx.scene.layout.VBox; +import javafx.scene.text.Text; public class PlayerInfoWidget { private final GameInformation.Player information; private final VBox container; + private Text playerName; + private boolean hasSet; public PlayerInfoWidget(GameInformation.Player information) { this.information = information; @@ -16,6 +19,7 @@ public class PlayerInfoWidget { buildToggle().getNode(), buildContent() ); + this.playerName = null; } private ToggleWidget buildToggle() { @@ -33,51 +37,69 @@ public class PlayerInfoWidget { } private Node buildContent() { - if (information.isHuman) { - var nameInput = new LabeledInputWidget( - "name", - "enter-your-name", - information.name, - newName -> information.name = newName - ); + if (information.isHuman) { + var nameInput = new LabeledInputWidget( + "name", + "enter-your-name", + information.name, + newName -> information.name = newName + ); - return nameInput.getNode(); - } else { - if (information.name == null || information.name.isEmpty()) { - information.name = "Pism Bot"; - } + return nameInput.getNode(); + } else { + var AIBox = Primitive.vbox( + makeAIButton(0, 1, "zwartepiet"), + makeAIButton(2, 1, "sinterklaas"), + makeAIButton(9, 1, "santa") + ); - var playerName = Primitive.text(""); - playerName.setText(information.name); + this.playerName = Primitive.text(""); + playerName.setText(information.name); - var nameDisplay = Primitive.vbox( - Primitive.text("name"), - playerName - ); + var nameDisplay = Primitive.vbox( + Primitive.text("name"), + playerName + ); - var difficultySlider = new LabeledSliderWidget( - "computer-difficulty", - 0, 5, - information.computerDifficulty, - newVal -> information.computerDifficulty = newVal - ); + if (!hasSet) { + doDefault(); + hasSet = true; + } - var thinkTimeSlider = new LabeledSliderWidget( - "computer-think-time", - 0, 5, - information.computerThinkTime, - newVal -> information.computerThinkTime = newVal - ); + return Primitive.vbox( + AIBox, + nameDisplay + ); - return Primitive.vbox( - nameDisplay, - difficultySlider.getNode(), - thinkTimeSlider.getNode() - ); - } - } + } + } public Node getNode() { return container; } + + private Node makeAIButton(int depth, int thinktime, String name) { + return Primitive.button(name, () -> { + information.name = getName(name); + information.computerDifficulty = depth; + information.computerThinkTime = thinktime; + this.playerName.setText(getName(name)); + }); + } + + private String getName(String name) { + return switch (name) { + case "sinterklaas" -> "Sint. R. Klaas"; + case "zwartepiet" -> "Zwarte Piet"; + case "santa" -> "Santa"; + default -> "Default"; + }; + } + + private void doDefault() { + information.name = getName("zwartepiet"); + information.computerDifficulty = 0; + information.computerThinkTime = 1; + this.playerName.setText(getName("zwartepiet")); + } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java index cbbf702..4e5cc3a 100644 --- a/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java +++ b/app/src/main/java/org/toop/app/widget/view/LocalMultiplayerView.java @@ -52,12 +52,12 @@ public class LocalMultiplayerView extends ViewWidget { if (information.players[0].isHuman) { players[0] = new LocalPlayer<>(information.players[0].name); } else { - players[0] = new ArtificialPlayer<>(new TicTacToeAIR(), information.players[0].name); + players[0] = new ArtificialPlayer<>(new TicTacToeAIR(information.players[0].computerDifficulty), information.players[0].name); } if (information.players[1].isHuman) { players[1] = new LocalPlayer<>(information.players[1].name); } else { - players[1] = new ArtificialPlayer<>(new TicTacToeAIR(), information.players[1].name); + players[1] = new ArtificialPlayer<>(new TicTacToeAIR(information.players[1].computerDifficulty), information.players[1].name); } if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstTTT()) { new ShowEnableTutorialWidget( diff --git a/app/src/main/resources/localization/localization_ar.properties b/app/src/main/resources/localization/localization_ar.properties index 215ee4c..42c4a80 100644 --- a/app/src/main/resources/localization/localization_ar.properties +++ b/app/src/main/resources/localization/localization_ar.properties @@ -86,6 +86,9 @@ tutorialstring=\u0627\u0644\u062f\u0631\u0633 \u0627\u0644\u062a\u0648\u0636\u06 startgame=\u0627\u0628\u062f\u0623 \u0627\u0644\u0644\u0639\u0628\u0629! goback=\u0627\u0631\u062c\u0639 turnof=\u062F\u0648\u0631\u0647 +zwartepiet=\u0633\u0647\u0644: Zwarte Piet +sinterklaas=\u0645\u062a\u0648\u0633\u0637: Sint R. Klaas +santa=\u0635\u0639\u0628: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 diff --git a/app/src/main/resources/localization/localization_de.properties b/app/src/main/resources/localization/localization_de.properties index 3a64fdc..20a0afa 100644 --- a/app/src/main/resources/localization/localization_de.properties +++ b/app/src/main/resources/localization/localization_de.properties @@ -88,6 +88,9 @@ tutorialstring=Tutorial startgame=Spiel starten! goback=Zurück turnof=ist dran +zwartepiet=Leicht: Zwarte Piet +sinterklaas=Mittel: Sint R. Klaas +santa=Schwer: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch) chinese=\u4e2d\u6587 (Chinesisch) diff --git a/app/src/main/resources/localization/localization_en.properties b/app/src/main/resources/localization/localization_en.properties index 72c8c2d..2fa4442 100644 --- a/app/src/main/resources/localization/localization_en.properties +++ b/app/src/main/resources/localization/localization_en.properties @@ -89,6 +89,9 @@ tutorialstring=Tutorial startgame=Start game! goback=Go back turnof='s turn +zwartepiet=Easy: Zwarte Piet +sinterklaas=Medium: Sint R. Klaas +santa=Hard:Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabic) chinese=\u4e2d\u6587 (Chinese) diff --git a/app/src/main/resources/localization/localization_es.properties b/app/src/main/resources/localization/localization_es.properties index dee7fd8..0096339 100644 --- a/app/src/main/resources/localization/localization_es.properties +++ b/app/src/main/resources/localization/localization_es.properties @@ -87,7 +87,9 @@ tutorialstring=Tutorial startgame=\u00a1Iniciar juego! goback=Volver turnof=le toca - +zwartepiet=F\u00e1cil: Zwarte Piet +sinterklaas=Medio: Sint R. Klaas +santa=Dif\u00edcil: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Ar\u00e1bigo) chinese=\u4e2d\u6587 (Chino) diff --git a/app/src/main/resources/localization/localization_fr.properties b/app/src/main/resources/localization/localization_fr.properties index 8653a49..96337b0 100644 --- a/app/src/main/resources/localization/localization_fr.properties +++ b/app/src/main/resources/localization/localization_fr.properties @@ -87,6 +87,9 @@ tutorialstring=Tutoriel startgame=D\u00e9marrer le jeu! goback=Retour turnof=\u00E0 son tour +zwartepiet=Facile: Zwarte Piet +sinterklaas=Moyen : Sint R. Klaas +santa=Difficile: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabe) chinese=\u4e2d\u6587 (Chinois) diff --git a/app/src/main/resources/localization/localization_hi.properties b/app/src/main/resources/localization/localization_hi.properties index 36d733f..4a11271 100644 --- a/app/src/main/resources/localization/localization_hi.properties +++ b/app/src/main/resources/localization/localization_hi.properties @@ -87,6 +87,9 @@ tutorialstring=\u0924\u0942\u091f\u0949\u0930\u093f\u092f\u0932 startgame=\u0916\u0947\u0932 \u0936\u0941\u0930\u0942 \u0915\u0930\u0947\u0902! goback=\u0935\u093e\u092a\u0938 \u091c\u093e\u090f\u0901 turnof=\u0915\u0940 \u092C\u093E\u0930\u0940 +zwartepiet=\u0905\u0938\u093e\u0928: Zwarte Piet +sinterklaas=\u092e\u0927\u094d\u092f\u092e: Sint R. Klaas +santa=\u0915\u0924\u093f\u0928: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0905\u0930\u092c\u0940) chinese=\u4e2d\u6587 (\u091a\u0940\u0928\u0940) diff --git a/app/src/main/resources/localization/localization_it.properties b/app/src/main/resources/localization/localization_it.properties index 34b691e..d83b12f 100644 --- a/app/src/main/resources/localization/localization_it.properties +++ b/app/src/main/resources/localization/localization_it.properties @@ -86,6 +86,9 @@ tutorialstring=Tutorial startgame=Avvia il gioco! goback=Indietro turnof=\u00E8 il suo turno +zwartepiet=Facile: Zwarte Piet +sinterklaas=Medio: Sint R. Klaas +santa=Difficile: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabo) chinese=\u4e2d\u6587 (Cinese) diff --git a/app/src/main/resources/localization/localization_ja.properties b/app/src/main/resources/localization/localization_ja.properties index 8bd3e37..769f939 100644 --- a/app/src/main/resources/localization/localization_ja.properties +++ b/app/src/main/resources/localization/localization_ja.properties @@ -86,6 +86,9 @@ tutorialstring=\u30c1\u30e5\u30fc\u30c8\u30ea\u30a2\u30eb startgame=\u30b2\u30fc\u30e0\u3092\u958b\u59cb\uff01 goback=\u623b\u308b turnof=\u306E\u756A +zwartepiet=\u7c21\u5358: Zwarte Piet +sinterklaas=\u4e2d\u7d1a: Sint R. Klaas +santa=\u96e3\u3057\u3044: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u30a2\u30e9\u30d3\u30a2\u8a9e) chinese=\u4e2d\u6587 (\u4e2d\u6587) diff --git a/app/src/main/resources/localization/localization_ko.properties b/app/src/main/resources/localization/localization_ko.properties index 7284b6a..aa1c0da 100644 --- a/app/src/main/resources/localization/localization_ko.properties +++ b/app/src/main/resources/localization/localization_ko.properties @@ -86,6 +86,9 @@ tutorialstring=\ud14c\ud2b8\ub9ad startgame=\uac8c\uc784 \uc2dc\uc791! goback=\ub4a4\ub85c \uac00\uae30 turnof=\uC758 \uCC28\uB840 +zwartepiet=\uc218\uc601: Zwarte Piet +sinterklaas=\ubcf4\ud1b5: Sint R. Klaas +santa=\uc5d0\uc18c: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0639\u0631\u0628\u064a\u0629) chinese=\u4e2d\u6587 (\u4e2d\u6587) diff --git a/app/src/main/resources/localization/localization_nl.properties b/app/src/main/resources/localization/localization_nl.properties index 451e8cd..1be45ea 100644 --- a/app/src/main/resources/localization/localization_nl.properties +++ b/app/src/main/resources/localization/localization_nl.properties @@ -86,6 +86,9 @@ tutorialstring=Tutorial startgame=Spel starten! goback=Ga terug turnof=is aan de beurt +zwartepiet=Makkelijk: Zwarte Piet +sinterklaas=Gemiddeld: Sint R. Klaas +santa=Moeilijk: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (Arabisch) chinese=\u4e2d\u6587 (Chinees) diff --git a/app/src/main/resources/localization/localization_ru.properties b/app/src/main/resources/localization/localization_ru.properties index 437742f..cc0ab81 100644 --- a/app/src/main/resources/localization/localization_ru.properties +++ b/app/src/main/resources/localization/localization_ru.properties @@ -86,6 +86,9 @@ tutorialstring=\u0423\u0447\u0435\u0431\u043d\u0438\u043a startgame=\u041d\u0430\u0447\u0430\u0442\u044c \u0438\u0433\u0440\u0443! goback=\u041d\u0430\u0437\u0430\u0434 turnof=\u0445\u043E\u0434\u0438\u0442 +zwartepiet=\u041b\u0435\u0433\u043a\u043e: Zwarte Piet +sinterklaas=\u0421\u0440\u0435\u0434\u043d\u0438\u0439: Sint R. Klaas +santa=\u0421\u043b\u043e\u0436\u043d\u043e: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u0410\u0440\u0430\u0431\u0441\u043a\u0438\u0439) chinese=\u4e2d\u6587 (\u041a\u0438\u0442\u0430\u0439\u0441\u043a\u0438\u0439) diff --git a/app/src/main/resources/localization/localization_zh.properties b/app/src/main/resources/localization/localization_zh.properties index 96d97c6..33dce40 100644 --- a/app/src/main/resources/localization/localization_zh.properties +++ b/app/src/main/resources/localization/localization_zh.properties @@ -86,6 +86,9 @@ tutorialstring=\u6559\u7a0b startgame=\u5f00\u59cb\u6e38\u620f\uff01 goback=\u8fd4\u56de turnof=\u7684\u56DE\u5408 +zwartepiet=\u7b80\u5355: Zwarte Piet +sinterklaas=\u4e2d\u7b49: Sint R. Klaas +santa=\u56f0\u96be: Santa arabic=\u0627\u0644\u0639\u0631\u0628\u064a\u0629 (\u963f\u62c9\u4f2f\u8bed) chinese=\u4e2d\u6587 diff --git a/game/src/main/java/org/toop/game/games/reversi/ReversiR.java b/game/src/main/java/org/toop/game/games/reversi/ReversiR.java index 56f5e28..85d6334 100644 --- a/game/src/main/java/org/toop/game/games/reversi/ReversiR.java +++ b/game/src/main/java/org/toop/game/games/reversi/ReversiR.java @@ -25,10 +25,10 @@ public final class ReversiR extends AbstractGame { // TODO: Don't hardcore for two players :) public record Score(int player1Score, int player2Score) {} - public ReversiR(Player[] players) { - super(8, 8, 2, players); + public ReversiR(Player[] players) { + super(8, 8, 2, players); addStartPieces(); - } + } public ReversiR(ReversiR other) { super(other); @@ -53,8 +53,8 @@ public final class ReversiR extends AbstractGame { } } - @Override - public int[] getLegalMoves() { + @Override + public int[] getLegalMoves() { final ArrayList legalMoves = new ArrayList<>(); int[][] boardGrid = makeBoardAGrid(); int currentPlayer = this.getCurrentTurn(); @@ -67,7 +67,7 @@ public final class ReversiR extends AbstractGame { } } return legalMoves.stream().mapToInt(Integer::intValue).toArray(); - } + } private Set getAdjacentCells(int[][] boardGrid) { Set possibleCells = new HashSet<>(); @@ -76,7 +76,7 @@ public final class ReversiR extends AbstractGame { for (int deltaRow = -1; deltaRow <= 1; deltaRow++){ //orthogonally and diagonally int newX = point.x + deltaColumn, newY = point.y + deltaRow; if (deltaColumn == 0 && deltaRow == 0 //continue if out of bounds - || !isOnBoard(newX, newY)) { + || !isOnBoard(newX, newY)) { continue; } if (boardGrid[newY][newX] == EMPTY) { //check if the cell is empty diff --git a/game/src/main/java/org/toop/game/games/tictactoe/TicTacToeAIR.java b/game/src/main/java/org/toop/game/games/tictactoe/TicTacToeAIR.java index 07271b1..faeba18 100644 --- a/game/src/main/java/org/toop/game/games/tictactoe/TicTacToeAIR.java +++ b/game/src/main/java/org/toop/game/games/tictactoe/TicTacToeAIR.java @@ -26,9 +26,15 @@ public final class TicTacToeAIR extends AbstractAI { * @param game the current Tic-Tac-Toe game state * @param depth the depth of lookahead for evaluating moves (non-negative) * @return the index of the best move, or -1 if no moves are available + * */ + + private int depth; + + public TicTacToeAIR(int depth) { + this.depth = depth; + } public int getMove(TicTacToeR game) { - int depth = 9; assert game != null; final int[] legalMoves = game.getLegalMoves(); diff --git a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAIRTest.java b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAIRTest.java index 72277cb..7809712 100644 --- a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAIRTest.java +++ b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAIRTest.java @@ -10,7 +10,7 @@ import static org.junit.jupiter.api.Assertions.*; final class TicTacToeAIRTest { - private final TicTacToeAIR ai = new TicTacToeAIR(); + private final TicTacToeAIR ai = new TicTacToeAIR(9); // Helper: play multiple moves in sequence on a fresh board private TicTacToeR playSequence(int... moves) {