From 477afa142bf819b1f96cb29509e89d7a3e07aecf Mon Sep 17 00:00:00 2001 From: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:25:21 +0200 Subject: [PATCH 1/5] UI (#106) * added localization options //todo add all the strings * broken push * merge to UI * broken push * Alpha rebase complete, added asset loader for UI branch * merge to UI * UI now uses assetmanager * added NL and EN for all strings currently in UI * fix small merge error * Removed no more needed files. * JDK25 * Removed files no longer in use * Removed need for manual typecast * Added ability to load in BundledResource for localization * Docs and easy font loading. Added interfaces for PreloadResource's * added German, French, Spanish, Italian and Mandarin Chinese --------- Co-authored-by: Ticho Hidding Co-authored-by: ramollia <@> --- .idea/misc.xml | 2 +- .idea/resourceBundles.xml | 15 ++++++++++ app/src/main/java/org/toop/app/App.java | 1 - .../main/java/org/toop/app/menu/MainMenu.java | 3 +- .../main/resources/Localization.properties | 17 ----------- .../main/resources/Localization_nl.properties | 17 ----------- .../localization/localization.properties | 4 +-- .../localization/localization_de.properties | 17 +++++++++++ .../localization/localization_es.properties | 17 +++++++++++ .../localization/localization_fr.properties | 17 +++++++++++ .../localization/localization_it.properties | 17 +++++++++++ .../localization/localization_zh.properties | 30 +++++++++++++++++++ .../org/toop/framework/asset/AssetLoader.java | 3 +- .../toop/framework/asset/AssetManager.java | 2 +- 14 files changed, 121 insertions(+), 41 deletions(-) create mode 100644 .idea/resourceBundles.xml delete mode 100644 app/src/main/resources/Localization.properties delete mode 100644 app/src/main/resources/Localization_nl.properties create mode 100644 app/src/main/resources/assets/localization/localization_de.properties create mode 100644 app/src/main/resources/assets/localization/localization_es.properties create mode 100644 app/src/main/resources/assets/localization/localization_fr.properties create mode 100644 app/src/main/resources/assets/localization/localization_it.properties create mode 100644 app/src/main/resources/assets/localization/localization_zh.properties diff --git a/.idea/misc.xml b/.idea/misc.xml index 72be14a..64c32f6 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -13,7 +13,7 @@ - + \ No newline at end of file diff --git a/.idea/resourceBundles.xml b/.idea/resourceBundles.xml new file mode 100644 index 0000000..362dcdf --- /dev/null +++ b/.idea/resourceBundles.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + localization + + + \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 051cdfc..0602577 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -10,7 +10,6 @@ import javafx.scene.Scene; import javafx.stage.Stage; import org.toop.framework.asset.AssetManager; import org.toop.framework.asset.resources.LocalizationAsset; -import org.toop.framework.audio.SoundManager; import org.toop.framework.audio.events.AudioEvents; import org.toop.framework.eventbus.EventFlow; import org.toop.local.AppContext; diff --git a/app/src/main/java/org/toop/app/menu/MainMenu.java b/app/src/main/java/org/toop/app/menu/MainMenu.java index 7d1d2d0..68f58a6 100644 --- a/app/src/main/java/org/toop/app/menu/MainMenu.java +++ b/app/src/main/java/org/toop/app/menu/MainMenu.java @@ -12,9 +12,10 @@ import java.util.Locale; import org.toop.framework.asset.AssetManager; import org.toop.framework.asset.resources.CssAsset; import org.toop.framework.asset.resources.ImageAsset; +import org.toop.local.AppContext; public final class MainMenu extends Menu { - private final Locale currentLocale = Locale.of("nl"); + private final Locale currentLocale = AppContext.getLocale(); private final LocalizationAsset loc = AssetManager.get("localization.properties"); public MainMenu() { diff --git a/app/src/main/resources/Localization.properties b/app/src/main/resources/Localization.properties deleted file mode 100644 index 9718bf0..0000000 --- a/app/src/main/resources/Localization.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Window title -windowTitle=ISY Games Selector - -# Main Menu buttons -mainMenuSelectTicTacToe=Tic Tac Toe -mainMenuSelectReversi=Reversi -mainMenuSelectSudoku=Sudoku -mainMenuSelectBattleship=Battleship -mainMenuSelectOther=Other -mainMenuSelectCredits=Credits -mainMenuSelectOptions=Options -mainMenuSelectQuit=Quit - -# Quit Menu text and buttons -quitMenuTextSure=Are you sure? -quitMenuButtonYes=Yes -quitMenuButtonNo=No \ No newline at end of file diff --git a/app/src/main/resources/Localization_nl.properties b/app/src/main/resources/Localization_nl.properties deleted file mode 100644 index 4c3eb30..0000000 --- a/app/src/main/resources/Localization_nl.properties +++ /dev/null @@ -1,17 +0,0 @@ -# Window title -windowTitle=ISY Spellen Kiezer - -# Main Menu buttons -mainMenuSelectTicTacToe=Boter Kaas En Eieren -mainMenuSelectReversi=Reversi -mainMenuSelectSudoku=Sudoku -mainMenuSelectBattleship=Zeeslag -mainMenuSelectOther=Anders -mainMenuSelectCredits=Credits -mainMenuSelectOptions=Opties -mainMenuSelectQuit=Afsluiten - -# Quit Menu text and buttons -quitMenuTextSure=Weet je het zeker? -quitMenuButtonYes=Ja -quitMenuButtonNo=Nee \ No newline at end of file diff --git a/app/src/main/resources/assets/localization/localization.properties b/app/src/main/resources/assets/localization/localization.properties index 9718bf0..fcbc8ac 100644 --- a/app/src/main/resources/assets/localization/localization.properties +++ b/app/src/main/resources/assets/localization/localization.properties @@ -2,8 +2,8 @@ windowTitle=ISY Games Selector # Main Menu buttons -mainMenuSelectTicTacToe=Tic Tac Toe -mainMenuSelectReversi=Reversi +mainMenuSelectTicTacToe=Tic Tac Toe\u5426 +mainMenuSelectReversi=Reversi\u5426 mainMenuSelectSudoku=Sudoku mainMenuSelectBattleship=Battleship mainMenuSelectOther=Other diff --git a/app/src/main/resources/assets/localization/localization_de.properties b/app/src/main/resources/assets/localization/localization_de.properties new file mode 100644 index 0000000..dc46278 --- /dev/null +++ b/app/src/main/resources/assets/localization/localization_de.properties @@ -0,0 +1,17 @@ +# Window title +windowTitle=ISY Spiele-Auswahl + +# Main Menu buttons +mainMenuSelectTicTacToe=Tic Tac Toe +mainMenuSelectReversi=Reversi +mainMenuSelectSudoku=Sudoku +mainMenuSelectBattleship=Flottenman\u00F6ver +mainMenuSelectOther=Andere +mainMenuSelectCredits=Credits +mainMenuSelectOptions=Optionen +mainMenuSelectQuit=Beenden + +# Quit Menu text and buttons +quitMenuTextSure=Bist du sicher? +quitMenuButtonYes=Ja +quitMenuButtonNo=Nein \ No newline at end of file diff --git a/app/src/main/resources/assets/localization/localization_es.properties b/app/src/main/resources/assets/localization/localization_es.properties new file mode 100644 index 0000000..60439ab --- /dev/null +++ b/app/src/main/resources/assets/localization/localization_es.properties @@ -0,0 +1,17 @@ +# Window title +windowTitle=Selector de juegos ISY + +# Main Menu buttons +mainMenuSelectTicTacToe=Tres en raya +mainMenuSelectReversi=Reversi +mainMenuSelectSudoku=Sudoku +mainMenuSelectBattleship=Batalla naval +mainMenuSelectOther=Otros +mainMenuSelectCredits=Cr\u00E9ditos +mainMenuSelectOptions=Opciones +mainMenuSelectQuit=Salir + +# Quit Menu text and buttons +quitMenuTextSure=\u00BFEst\u00E1s seguro? +quitMenuButtonYes=S\u00ED +quitMenuButtonNo=No \ No newline at end of file diff --git a/app/src/main/resources/assets/localization/localization_fr.properties b/app/src/main/resources/assets/localization/localization_fr.properties new file mode 100644 index 0000000..75d9f4a --- /dev/null +++ b/app/src/main/resources/assets/localization/localization_fr.properties @@ -0,0 +1,17 @@ +# Window title +windowTitle=S\u00E9lecteur de jeux ISY + +# Main Menu buttons +mainMenuSelectTicTacToe=Morpion +mainMenuSelectReversi=Reversi +mainMenuSelectSudoku=Sudoku +mainMenuSelectBattleship=Bataille navale +mainMenuSelectOther=Autres +mainMenuSelectCredits=Cr\u00E9dits +mainMenuSelectOptions=Options +mainMenuSelectQuit=Quitter + +# Quit Menu text and buttons +quitMenuTextSure=\u00CAtes-vous s\u00FBr? +quitMenuButtonYes=Oui +quitMenuButtonNo=Non \ No newline at end of file diff --git a/app/src/main/resources/assets/localization/localization_it.properties b/app/src/main/resources/assets/localization/localization_it.properties new file mode 100644 index 0000000..75a8896 --- /dev/null +++ b/app/src/main/resources/assets/localization/localization_it.properties @@ -0,0 +1,17 @@ +# Window title +windowTitle=Selettore giochi ISY + +# Main Menu buttons +mainMenuSelectTicTacToe=Tris +mainMenuSelectReversi=Reversi +mainMenuSelectSudoku=Sudoku +mainMenuSelectBattleship=Battaglia navale +mainMenuSelectOther=Altro +mainMenuSelectCredits=Crediti +mainMenuSelectOptions=Opzioni +mainMenuSelectQuit=Esci + +# Quit Menu text and buttons +quitMenuTextSure=Sei sicuro? +quitMenuButtonYes=S\u00EC +quitMenuButtonNo=No \ No newline at end of file diff --git a/app/src/main/resources/assets/localization/localization_zh.properties b/app/src/main/resources/assets/localization/localization_zh.properties new file mode 100644 index 0000000..f703670 --- /dev/null +++ b/app/src/main/resources/assets/localization/localization_zh.properties @@ -0,0 +1,30 @@ +# suppress inspection "LossyEncoding" for whole file +# Window title +windowTitle=ISY \u6E38\u620F\u9009\u62E9\u5668 +# ????? + +# Main Menu buttons +mainMenuSelectTicTacToe=\u4E95\u5B57\u68CB +# ??? +mainMenuSelectReversi=\u9ED1\u767D\u68CB +# ??? +mainMenuSelectSudoku=\u6570\u72EC +# ?? +mainMenuSelectBattleship=\u6D77\u6218\u68CB +# ??? +mainMenuSelectOther=\u5176\u4ED6 +# ?? +mainMenuSelectCredits=\u5236\u4F5C\u4EBA\u5458 +# ???? +mainMenuSelectOptions=\u9009\u9879 +# ?? +mainMenuSelectQuit=\u9000\u51FA +# ?? + +# Quit Menu text and buttons +quitMenuTextSure=\u4F60\u786E\u5B9A\u5417\uFF1F +# ????? +quitMenuButtonYes=\u662F +# ? +quitMenuButtonNo=\u5426 +# ? \ No newline at end of file diff --git a/framework/src/main/java/org/toop/framework/asset/AssetLoader.java b/framework/src/main/java/org/toop/framework/asset/AssetLoader.java index bec2c97..93ac189 100644 --- a/framework/src/main/java/org/toop/framework/asset/AssetLoader.java +++ b/framework/src/main/java/org/toop/framework/asset/AssetLoader.java @@ -144,6 +144,7 @@ public class AssetLoader { "File " + file.getName() + " is not of type " + type.getSimpleName() ); } + return type.cast(resource); } @@ -242,4 +243,4 @@ public class AssetLoader { int i = name.lastIndexOf('.'); return (i > 0) ? name.substring(i + 1) : ""; } -} \ No newline at end of file +} diff --git a/framework/src/main/java/org/toop/framework/asset/AssetManager.java b/framework/src/main/java/org/toop/framework/asset/AssetManager.java index f10d507..1630ae0 100644 --- a/framework/src/main/java/org/toop/framework/asset/AssetManager.java +++ b/framework/src/main/java/org/toop/framework/asset/AssetManager.java @@ -147,4 +147,4 @@ public class AssetManager { public static void addAsset(Asset asset) { assets.put(asset.getName(), asset); } -} \ No newline at end of file +} From a6bd2495ab9a300ebac6dc7037596d93a01b2132 Mon Sep 17 00:00:00 2001 From: Bas Antonius de Jong <49651652+BAFGdeJong@users.noreply.github.com> Date: Thu, 2 Oct 2025 19:34:10 +0200 Subject: [PATCH 2/5] UI (#107) * added localization options //todo add all the strings * broken push * merge to UI * broken push * Alpha rebase complete, added asset loader for UI branch * merge to UI * UI now uses assetmanager * added NL and EN for all strings currently in UI * fix small merge error * Removed no more needed files. * JDK25 * Removed files no longer in use * Removed need for manual typecast * Added ability to load in BundledResource for localization * Docs and easy font loading. Added interfaces for PreloadResource's * added German, French, Spanish, Italian and Mandarin Chinese --------- Co-authored-by: Ticho Hidding Co-authored-by: ramollia <@> From e48df5df1595c482457bd828ee8b750d0b8efbb2 Mon Sep 17 00:00:00 2001 From: lieght <49651652+BAFGdeJong@users.noreply.github.com> Date: Fri, 3 Oct 2025 00:58:57 +0200 Subject: [PATCH 3/5] More gitignore --- .gitignore | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index b561d9a..592a652 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ shelf/ *.iml *.ipr *.iws +misc.xml ############################## ## Eclipse @@ -101,4 +102,9 @@ build newgamesver-release-V1.jar server.properties gameserver.log.* -gameserver.log \ No newline at end of file +gameserver.log + +############################## +## JPackage +############################## +jpackage-input \ No newline at end of file From efca808e1cf3a5bb1ce6b1fbd6915b03972696fd Mon Sep 17 00:00:00 2001 From: ramollia <@> Date: Fri, 3 Oct 2025 01:33:49 +0200 Subject: [PATCH 4/5] small amount of refactoring. might break everything :). --- app/src/main/java/org/toop/app/App.java | 9 +- .../java/org/toop/app/canvas/GameCanvas.java | 77 ++++++++ .../org/toop/app/canvas/TicTacToeCanvas.java | 81 +++++++++ .../main/java/org/toop/app/menu/MainMenu.java | 4 +- app/src/main/java/org/toop/app/menu/Menu.java | 1 - .../org/toop/app/menu/game/TicTacToeMenu.java | 133 -------------- game/src/main/java/org/toop/game/Game.java | 31 +--- game/src/main/java/org/toop/game/Player.java | 3 - .../java/org/toop/game/TurnBasedGame.java | 26 +++ .../org/toop/game/tictactoe/TicTacToe.java | 56 +++--- .../test/java/org/toop/game/PlayerTest.java | 48 ----- .../toop/game/tictactoe/TicTacToeAITest.java | 166 +++++++++--------- 12 files changed, 302 insertions(+), 333 deletions(-) create mode 100644 app/src/main/java/org/toop/app/canvas/GameCanvas.java create mode 100644 app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java delete mode 100644 app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java delete mode 100644 game/src/main/java/org/toop/game/Player.java create mode 100644 game/src/main/java/org/toop/game/TurnBasedGame.java delete mode 100644 game/src/test/java/org/toop/game/PlayerTest.java diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index bf797b6..a3b29c7 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -1,5 +1,6 @@ package org.toop.app; +import org.toop.app.canvas.TicTacToeCanvas; import org.toop.app.menu.MainMenu; import org.toop.app.menu.Menu; @@ -47,7 +48,6 @@ public final class App extends Application { pane = new StackPane(background, box); pane.getStylesheets().add(ResourceManager.get(CssAsset.class, "quit.css").getUrl()); - } } @@ -60,7 +60,7 @@ public final class App extends Application { final StackPane root = new StackPane(new MainMenu().getPane()); final Scene scene = new Scene(root); - scene.getStylesheets().add(((CssAsset) ResourceManager.get("app.css")).getUrl()); + scene.getStylesheets().add(((CssAsset)ResourceManager.get("app.css")).getUrl()); stage.setTitle("pism"); stage.setMinWidth(1080); @@ -85,10 +85,13 @@ public final class App extends Application { App.width = (int)stage.getWidth(); App.height = (int)stage.getHeight(); + App.isQuitting = false; + new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent(); new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(0.1)).asyncPostEvent(); - App.isQuitting = false; + TicTacToeCanvas canvas = new TicTacToeCanvas(); + root.getChildren().addLast(canvas.getCanvas()); } public static void quitPopup() { diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java new file mode 100644 index 0000000..0255504 --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -0,0 +1,77 @@ +package org.toop.app.canvas; + +import javafx.scene.canvas.Canvas; +import javafx.scene.canvas.GraphicsContext; +import javafx.scene.input.MouseButton; + +public abstract class GameCanvas { + protected record Cell(float x, float y, float width, float height) {} + + protected final int width; + protected final int height; + + protected final Canvas canvas; + protected final GraphicsContext graphics; + + protected final int rows; + protected final int columns; + + protected final int gapSize; + + protected final Cell[] cells; + + protected GameCanvas(int width, int height, int rows, int columns, int gapSize) { + final Canvas canvas = new Canvas(width, height); + final GraphicsContext graphics = canvas.getGraphicsContext2D(); + + final Cell[] cells = new Cell[rows * columns]; + + final float cellWidth = ((float)width - (rows - 1) * gapSize) / rows; + final float cellHeight = ((float)height - (columns - 1) * gapSize) / columns; + + for (int y = 0; y < columns; y++) { + final float startY = y * cellHeight + y * gapSize; + + for (int x = 0; x < rows; x++) { + final float startX = x * cellWidth + x * gapSize; + cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight); + } + } + + canvas.setOnMouseClicked(event -> { + final MouseButton button = event.getButton(); + + if (button != MouseButton.PRIMARY && button != MouseButton.SECONDARY) { + return; + } + + final int column = (int)((event.getX() / width) * rows); + final int row = (int)((event.getY() / height) * columns); + + event.consume(); + onCellClicked(row * rows + column, button == MouseButton.PRIMARY); + }); + + this.width = width; + this.height = height; + + this.canvas = canvas; + this.graphics = graphics; + + this.rows = rows; + this.columns = columns; + + this.gapSize = gapSize; + + this.cells = cells; + } + + protected void clearCell(int cell) { + assert cell >= 0 && cell < cells.length; + graphics.clearRect(cells[cell].x(), cells[cell].y(), cells[cell].width(), cells[cell].height()); + } + + protected abstract void onCellClicked(int cell, boolean primary); + + public Canvas getCanvas() { return canvas; } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java new file mode 100644 index 0000000..dfe7325 --- /dev/null +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java @@ -0,0 +1,81 @@ +package org.toop.app.canvas; + +import javafx.scene.paint.Color; +import org.toop.app.App; +import org.toop.game.Game; +import org.toop.game.tictactoe.TicTacToe; + +public class TicTacToeCanvas extends GameCanvas { + private final TicTacToe game; + + public TicTacToeCanvas() { + super(App.getHeight(), App.getHeight(), 3, 3, 10); + game = new TicTacToe(); + + graphics.setFill(Color.CYAN); + + for (int x = 1; x < rows; x++) { + graphics.fillRect(cells[x].x() - gapSize, 0, gapSize, height); + } + + for (int y = 1; y < columns; y++) { + graphics.fillRect(0, cells[y * rows].y() - gapSize, width, gapSize); + } + } + + public void placeX(int cell) { + graphics.setStroke(Color.ORANGERED); + graphics.setLineWidth(gapSize); + + final float x = cells[cell].x() + gapSize; + final float y = cells[cell].y() + gapSize; + + final float width = cells[cell].width() - gapSize * 2; + final float height = cells[cell].height() - gapSize * 2; + + graphics.strokeLine(x, y, x + width, y + height); + graphics.strokeLine(x + width, y, x, y + height); + } + + public void placeO(int cell) { + graphics.setStroke(Color.DEEPSKYBLUE); + graphics.setLineWidth(gapSize); + + final float x = cells[cell].x() + gapSize; + final float y = cells[cell].y() + gapSize; + + final float width = cells[cell].width() - gapSize * 2; + final float height = cells[cell].height() - gapSize * 2; + + graphics.strokeOval(x, y, width, height); + } + + @Override + protected void onCellClicked(int cell, boolean primary) { + for (final Game.Move move : game.getLegalMoves()) { + if (move.position() == cell) { + if (move.value() == 'X') { + placeX(cell); + } else { + placeO(cell); + } + + final Game.State state = game.play(move); + + if (state == Game.State.WIN) { + graphics.setFill(Color.GREEN); + graphics.fillRect(cells[4].x(), cells[4].y(), cells[4].width(), cells[4].height()); + + for (int i = 0; i < game.board.length; i++) { + if (game.board[i] != move.value()) { + clearCell(i); + } + } + } else if (state == Game.State.DRAW) { + graphics.setFill(Color.DARKORANGE); + graphics.fillRect(cells[4].x(), cells[4].y(), cells[4].width(), cells[4].height()); + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/menu/MainMenu.java b/app/src/main/java/org/toop/app/menu/MainMenu.java index c369806..f170ded 100644 --- a/app/src/main/java/org/toop/app/menu/MainMenu.java +++ b/app/src/main/java/org/toop/app/menu/MainMenu.java @@ -6,14 +6,12 @@ import org.toop.app.GameType; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.layout.*; -import org.toop.app.menu.game.TicTacToeMenu; -import org.toop.game.tictactoe.TicTacToe; public final class MainMenu extends Menu { public MainMenu() { final Region background = createBackground(); - final Button tictactoe = createButton("Tic Tac Toe", () -> { App.activate(new TicTacToeMenu(new TicTacToe("player 1", true, "player 2", true))); }); + final Button tictactoe = createButton("Tic Tac Toe", () -> { App.activate(new GameSelectMenu(GameType.TICTACTOE)); }); final Button reversi = createButton("Reversi", () -> { App.activate(new GameSelectMenu(GameType.REVERSI)); }); final VBox gamesBox = new VBox(10, tictactoe, reversi); diff --git a/app/src/main/java/org/toop/app/menu/Menu.java b/app/src/main/java/org/toop/app/menu/Menu.java index 82c12ee..9e9ac29 100644 --- a/app/src/main/java/org/toop/app/menu/Menu.java +++ b/app/src/main/java/org/toop/app/menu/Menu.java @@ -1,6 +1,5 @@ package org.toop.app.menu; -import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; diff --git a/app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java b/app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java deleted file mode 100644 index 6295b7f..0000000 --- a/app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java +++ /dev/null @@ -1,133 +0,0 @@ -package org.toop.app.menu.game; - -import javafx.scene.paint.Color; -import org.toop.game.Game; -import org.toop.game.Player; -import org.toop.game.tictactoe.TicTacToe; -import org.toop.game.tictactoe.TicTacToeAI; - -import javax.management.RuntimeErrorException; -import java.util.concurrent.*; - -public final class TicTacToeMenu extends GameMenu { - private final TicTacToe game; - private final TicTacToeAI ai; - -// private final ExecutorService executor = Executors.newFixedThreadPool(1); - private final BlockingQueue moveQueue = new LinkedBlockingQueue<>(); - - public TicTacToeMenu(TicTacToe game) { - super(3, 3, 10); - - graphics.setFill(Color.CYAN); - - for (int x = 1; x < rows; x++) { - graphics.fillRect(cells[x].x - gapSize, 0, gapSize, size); - } - - for (int y = 1; y < columns; y++) { - graphics.fillRect(0, cells[y * rows].y - gapSize, size, gapSize); - } - - this.game = game; - ai = new TicTacToeAI(); - - canvas.setOnMouseClicked(event -> { - for (int i = 0; i < cells.length; i++) { - if (cells[i].check((float) event.getX(), (float) event.getY())) { - final Game.Move move = new Game.Move(i, game.getCurrentPlayer().values()[0]); - play(move); - } - } - }); - - new Thread(this::gameThread).start(); - } - - private void play(Game.Move move) { - final Game.Move[] legalMoves = game.getLegalMoves(); - - boolean isLegal = false; - - for (final Game.Move legalMove : legalMoves) { - if (legalMove.position() == move.position() && legalMove.value() == move.value()) { - isLegal = true; - break; - } - } - - if (!isLegal) { - return; - } - - try { moveQueue.put(move); } - catch (InterruptedException _) {} - } - - private void placeX(int cell) { - graphics.setStroke(Color.ORANGERED); - graphics.setLineWidth(gapSize); - - final float x = cells[cell].x + gapSize; - final float y = cells[cell].y + gapSize; - - final float width = cells[cell].width - gapSize * 2; - final float height = cells[cell].height - gapSize * 2; - - graphics.strokeLine(x, y, x + width, y + height); - graphics.strokeLine(x + width, y, x, y + height); - } - - private void placeO(int cell) { - graphics.setStroke(Color.DEEPSKYBLUE); - graphics.setLineWidth(gapSize); - - final float x = cells[cell].x + gapSize; - final float y = cells[cell].y + gapSize; - - final float width = cells[cell].width - gapSize * 2; - final float height = cells[cell].height - gapSize * 2; - - graphics.strokeOval(x, y, width, height); - } - - private void gameThread() { - boolean running = true; - - while(running) { - final Player currentPlayer = game.getCurrentPlayer(); - - try { - Game.Move move; - - if (!currentPlayer.isAI()) { - try { move = moveQueue.take(); } - catch (InterruptedException _) { return; } - } else { - move = ai.findBestMove(game, 9); - } - - assert move != null; - final Game.State state = game.play(move); - - if (move.value() == 'X') { - placeX(move.position()); - } else { - placeO(move.position()); - } - - switch (state) { - case NORMAL: break; - - case DRAW: - case LOSE: - case WIN: - running = false; - break; - } - } catch (RuntimeException e) { - return; - } - } - } -} \ No newline at end of file diff --git a/game/src/main/java/org/toop/game/Game.java b/game/src/main/java/org/toop/game/Game.java index a4220ed..71b2214 100644 --- a/game/src/main/java/org/toop/game/Game.java +++ b/game/src/main/java/org/toop/game/Game.java @@ -4,52 +4,31 @@ import java.util.Arrays; public abstract class Game { public enum State { - NORMAL, DRAW, LOSE, WIN, + NORMAL, DRAW, WIN, } public record Move(int position, char value) {} public static final char EMPTY = (char)0; - protected final int rowSize; - protected final int columnSize; - protected final char[] board; + public final int rowSize; + public final int columnSize; + public final char[] board; - protected final Player[] players; - protected int currentPlayer; - - protected Game(int rowSize, int columnSize, Player... players) { + protected Game(int rowSize, int columnSize) { assert rowSize > 0 && columnSize > 0; - assert players.length >= 1; this.rowSize = rowSize; this.columnSize = columnSize; board = new char[rowSize * columnSize]; Arrays.fill(board, EMPTY); - - this.players = players; - currentPlayer = 0; } protected Game(Game other) { rowSize = other.rowSize; columnSize = other.columnSize; board = Arrays.copyOf(other.board, other.board.length); - - players = Arrays.copyOf(other.players, other.players.length); - currentPlayer = other.currentPlayer; - } - - public int getRowSize() { return rowSize; } - public int getColumnSize() { return columnSize; } - public char[] getBoard() { return board; } - - public Player[] getPlayers() { return players; } - public Player getCurrentPlayer() { return players[currentPlayer]; } - - protected void nextPlayer() { - currentPlayer = (currentPlayer + 1) % players.length; } public abstract Move[] getLegalMoves(); diff --git a/game/src/main/java/org/toop/game/Player.java b/game/src/main/java/org/toop/game/Player.java deleted file mode 100644 index 22e46c1..0000000 --- a/game/src/main/java/org/toop/game/Player.java +++ /dev/null @@ -1,3 +0,0 @@ -package org.toop.game; - -public record Player(String name, boolean isAI, char... values) {} \ No newline at end of file diff --git a/game/src/main/java/org/toop/game/TurnBasedGame.java b/game/src/main/java/org/toop/game/TurnBasedGame.java new file mode 100644 index 0000000..4800abd --- /dev/null +++ b/game/src/main/java/org/toop/game/TurnBasedGame.java @@ -0,0 +1,26 @@ +package org.toop.game; + +public abstract class TurnBasedGame extends Game { + public final int turns; + + protected int currentTurn; + + protected TurnBasedGame(int rowSize, int columnSize, int turns) { + assert turns >= 2; + + super(rowSize, columnSize); + this.turns = turns; + } + + protected TurnBasedGame(TurnBasedGame other) { + super(other); + turns = other.turns; + currentTurn = other.currentTurn; + } + + protected void nextTurn() { + currentTurn = (currentTurn + 1) % turns; + } + + public int getCurrentTurn() { return currentTurn; } +} \ No newline at end of file diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java index dd50eeb..ee2a5b9 100644 --- a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java +++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java @@ -1,15 +1,14 @@ package org.toop.game.tictactoe; -import org.toop.game.Game; -import org.toop.game.Player; +import org.toop.game.TurnBasedGame; import java.util.ArrayList; -public final class TicTacToe extends Game { +public final class TicTacToe extends TurnBasedGame { private int movesLeft; - public TicTacToe(String player1, boolean isPlayer1AI, String player2, boolean isPlayer2AI) { - super(3, 3, new Player(player1, isPlayer1AI, 'X'), new Player(player2, isPlayer2AI, 'O')); + public TicTacToe() { + super(3, 3, 2); movesLeft = board.length; } @@ -20,11 +19,12 @@ public final class TicTacToe extends Game { @Override public Move[] getLegalMoves() { - final ArrayList legalMoves = new ArrayList<>(); + final ArrayList legalMoves = new ArrayList<>(); + final char currentValue = getCurrentValue(); for (int i = 0; i < board.length; i++) { if (board[i] == EMPTY) { - legalMoves.add(new Move(i, getCurrentPlayer().values()[0])); + legalMoves.add(new Move(i, currentValue)); } } @@ -35,7 +35,7 @@ public final class TicTacToe extends Game { public State play(Move move) { assert move != null; assert move.position() >= 0 && move.position() < board.length; - assert move.value() == getCurrentPlayer().values()[0]; + assert move.value() == getCurrentValue(); board[move.position()] = move.value(); movesLeft--; @@ -44,12 +44,12 @@ public final class TicTacToe extends Game { return State.WIN; } - nextPlayer(); + nextTurn(); if (movesLeft <= 2) { - if (checkDraw(new TicTacToe(this))) { - return State.DRAW; - } + if (movesLeft <= 0 || checkForEarlyDraw(this)) { + return State.DRAW; + } } return State.NORMAL; @@ -60,18 +60,14 @@ public final class TicTacToe extends Game { for (int i = 0; i < 3; i++) { final int index = i * 3; - if (board[index] != EMPTY - && board[index] == board[index + 1] - && board[index] == board[index + 2]) { + if (board[index] != EMPTY && board[index] == board[index + 1] && board[index] == board[index + 2]) { return true; } } // Vertical for (int i = 0; i < 3; i++) { - if (board[i] != EMPTY - && board[i] == board[i + 3] - && board[i] == board[i + 6]) { + if (board[i] != EMPTY && board[i] == board[i + 3] && board[i] == board[i + 6]) { return true; } } @@ -85,25 +81,19 @@ public final class TicTacToe extends Game { return board[2] != EMPTY && board[2] == board[4] && board[2] == board[6]; } - public boolean checkDraw(TicTacToe game) { - if (game.checkForWin()) { - return false; - } - if (game.movesLeft == 0) { - return true; - } - // try every move on a legal copy - for (Move move : game.getLegalMoves()) { - TicTacToe copy = new TicTacToe(game); - State result = copy.play(move); + private boolean checkForEarlyDraw(TicTacToe game) { + for (final Move move : game.getLegalMoves()) { + final TicTacToe copy = new TicTacToe(game); - if (result == State.WIN) { - return false; - } - if (!checkDraw(copy)) { + if (copy.play(move) == State.WIN || !checkForEarlyDraw(copy)) { return false; } } + return true; } + + private char getCurrentValue() { + return currentTurn == 0? 'X' : 'O'; + } } \ No newline at end of file diff --git a/game/src/test/java/org/toop/game/PlayerTest.java b/game/src/test/java/org/toop/game/PlayerTest.java deleted file mode 100644 index 298c84b..0000000 --- a/game/src/test/java/org/toop/game/PlayerTest.java +++ /dev/null @@ -1,48 +0,0 @@ -//package org.toop.game; -// -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -// -//import static org.junit.jupiter.api.Assertions.assertEquals; -// -//class PlayerTest { -// private Player playerA; -// private Player playerB; -// private Player playerC; -// -// @BeforeEach -// void setup() { -// playerA = new Player("test A", 'x', 'Z', 'i'); -// playerB = new Player("test B", 'O', (char)12, (char)-34, 's'); -// playerC = new Player("test C", (char)9, '9', (char)-9, '0', 'X', 'O'); -// } -// -// @Test -// void testNameGetter_returnsTrueForValidName() { -// assertEquals("test A", playerA.name()); -// assertEquals("test B", playerB.name()); -// assertEquals("test C", playerC.name()); -// } -// -// @Test -// void testValuesGetter_returnsTrueForValidValues() { -// final char[] valuesA = playerA.values(); -// assertEquals('x', valuesA[0]); -// assertEquals('Z', valuesA[1]); -// assertEquals('i', valuesA[2]); -// -// final char[] valuesB = playerB.values(); -// assertEquals('O', valuesB[0]); -// assertEquals(12, valuesB[1]); -// assertEquals((char)-34, valuesB[2]); -// assertEquals('s', valuesB[3]); -// -// final char[] valuesC = playerC.values(); -// assertEquals((char)9, valuesC[0]); -// assertEquals('9', valuesC[1]); -// assertEquals((char)-9, valuesC[2]); -// assertEquals('0', valuesC[3]); -// assertEquals('X', valuesC[4]); -// assertEquals('O', valuesC[5]); -// } -//} \ No newline at end of file diff --git a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java index fe15900..6be92f5 100644 --- a/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java +++ b/game/src/test/java/org/toop/game/tictactoe/TicTacToeAITest.java @@ -1,83 +1,83 @@ -//package org.toop.game.tictactoe; -// -//import org.toop.game.Game; -// -//import java.util.Set; -// -//import org.junit.jupiter.api.BeforeEach; -//import org.junit.jupiter.api.Test; -// -//import static org.junit.jupiter.api.Assertions.*; -// -//class TicTacToeAITest { -// private TicTacToe game; -// private TicTacToeAI ai; -// -// @BeforeEach -// void setup() { -// game = new TicTacToe("AI", "AI"); -// ai = new TicTacToeAI(); -// } -// -// @Test -// void testBestMove_returnWinningMoveWithDepth1() { -// // X X - -// // O O - -// // - - - -// game.play(new Game.Move(0, 'X')); -// game.play(new Game.Move(3, 'O')); -// game.play(new Game.Move(1, 'X')); -// game.play(new Game.Move(4, 'O')); -// -// final Game.Move move = ai.findBestMove(game, 1); -// -// assertNotNull(move); -// assertEquals('X', move.value()); -// assertEquals(2, move.position()); -// } -// -// @Test -// void testBestMove_blockOpponentWinDepth1() { -// // - - - -// // O - - -// // X X - -// game.play(new Game.Move(6, 'X')); -// game.play(new Game.Move(3, 'O')); -// game.play(new Game.Move(7, 'X')); -// -// final Game.Move move = ai.findBestMove(game, 1); -// -// assertNotNull(move); -// assertEquals('O', move.value()); -// assertEquals(8, move.position()); -// } -// -// @Test -// void testBestMove_preferCornerOnEmpty() { -// final Game.Move move = ai.findBestMove(game, 0); -// -// assertNotNull(move); -// assertEquals('X', move.value()); -// assertTrue(Set.of(0, 2, 6, 8).contains(move.position())); -// } -// -// @Test -// void testBestMove_findBestMoveDraw() { -// // O X - -// // - O X -// // X O X -// game.play(new Game.Move(1, 'X')); -// game.play(new Game.Move(0, 'O')); -// game.play(new Game.Move(5, 'X')); -// game.play(new Game.Move(4, 'O')); -// game.play(new Game.Move(6, 'X')); -// game.play(new Game.Move(7, 'O')); -// game.play(new Game.Move(8, 'X')); -// -// final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length); -// -// assertNotNull(move); -// assertEquals('O', move.value()); -// assertEquals(2, move.position()); -// } -//} \ No newline at end of file +package org.toop.game.tictactoe; + +import org.toop.game.Game; + +import java.util.Set; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class TicTacToeAITest { + private TicTacToe game; + private TicTacToeAI ai; + + @BeforeEach + void setup() { + game = new TicTacToe(); + ai = new TicTacToeAI(); + } + + @Test + void testBestMove_returnWinningMoveWithDepth1() { + // X X - + // O O - + // - - - + game.play(new Game.Move(0, 'X')); + game.play(new Game.Move(3, 'O')); + game.play(new Game.Move(1, 'X')); + game.play(new Game.Move(4, 'O')); + + final Game.Move move = ai.findBestMove(game, 1); + + assertNotNull(move); + assertEquals('X', move.value()); + assertEquals(2, move.position()); + } + + @Test + void testBestMove_blockOpponentWinDepth1() { + // - - - + // O - - + // X X - + game.play(new Game.Move(6, 'X')); + game.play(new Game.Move(3, 'O')); + game.play(new Game.Move(7, 'X')); + + final Game.Move move = ai.findBestMove(game, 1); + + assertNotNull(move); + assertEquals('O', move.value()); + assertEquals(8, move.position()); + } + + @Test + void testBestMove_preferCornerOnEmpty() { + final Game.Move move = ai.findBestMove(game, 0); + + assertNotNull(move); + assertEquals('X', move.value()); + assertTrue(Set.of(0, 2, 6, 8).contains(move.position())); + } + + @Test + void testBestMove_findBestMoveDraw() { + // O X - + // - O X + // X O X + game.play(new Game.Move(1, 'X')); + game.play(new Game.Move(0, 'O')); + game.play(new Game.Move(5, 'X')); + game.play(new Game.Move(4, 'O')); + game.play(new Game.Move(6, 'X')); + game.play(new Game.Move(7, 'O')); + game.play(new Game.Move(8, 'X')); + + final Game.Move move = ai.findBestMove(game, game.getLegalMoves().length); + + assertNotNull(move); + assertEquals('O', move.value()); + assertEquals(2, move.position()); + } +} \ No newline at end of file From 5e1d53e96b809343350426923595b3613dace762 Mon Sep 17 00:00:00 2001 From: ramollia <@> Date: Fri, 3 Oct 2025 01:41:11 +0200 Subject: [PATCH 5/5] tiny portion of refactoring --- app/src/main/java/org/toop/app/App.java | 6 +- .../java/org/toop/app/menu/game/GameMenu.java | 99 ------------------- .../main/java/org/toop/local/AppContext.java | 1 + 3 files changed, 5 insertions(+), 101 deletions(-) delete mode 100644 app/src/main/java/org/toop/app/menu/game/GameMenu.java diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index a3b29c7..83a1e38 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -60,7 +60,7 @@ public final class App extends Application { final StackPane root = new StackPane(new MainMenu().getPane()); final Scene scene = new Scene(root); - scene.getStylesheets().add(((CssAsset)ResourceManager.get("app.css")).getUrl()); + scene.getStylesheets().add(ResourceManager.get(CssAsset.class, "app.css").getUrl()); stage.setTitle("pism"); stage.setMinWidth(1080); @@ -90,7 +90,9 @@ public final class App extends Application { new EventFlow().addPostEvent(new AudioEvents.StartBackgroundMusic()).asyncPostEvent(); new EventFlow().addPostEvent(new AudioEvents.ChangeVolume(0.1)).asyncPostEvent(); - TicTacToeCanvas canvas = new TicTacToeCanvas(); + // Todo: Temp Obviously + // Replace with game of life + final TicTacToeCanvas canvas = new TicTacToeCanvas(); root.getChildren().addLast(canvas.getCanvas()); } diff --git a/app/src/main/java/org/toop/app/menu/game/GameMenu.java b/app/src/main/java/org/toop/app/menu/game/GameMenu.java deleted file mode 100644 index 4efa4ff..0000000 --- a/app/src/main/java/org/toop/app/menu/game/GameMenu.java +++ /dev/null @@ -1,99 +0,0 @@ -package org.toop.app.menu.game; - -import javafx.geometry.Pos; -import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.control.Button; -import javafx.scene.layout.HBox; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import javafx.scene.text.Text; -import org.toop.app.App; -import org.toop.app.menu.MainMenu; -import org.toop.app.menu.Menu; - -public abstract class GameMenu extends Menu { - protected final class Cell { - public float x; - public float y; - - public float width; - public float height; - - public Cell(float x, float y, float width, float height) { - this.x = x; - this.y = y; - - this.width = width; - this.height = height; - } - - public boolean check(float x, float y) { - return x >= this.x && y >= this.y && x <= this.x + width && y <= this.y + height; - } - } - - protected final int size; - - protected final Canvas canvas; - protected final GraphicsContext graphics; - - protected final int rows; - protected final int columns; - - protected final int gapSize; - - protected final Cell[] cells; - - protected GameMenu(int rows, int columns, int gapSize) { - final int size = Math.min(App.getWidth(), App.getHeight()) / 5 * 4; - - final Canvas canvas = new Canvas(size, size); - - final GraphicsContext graphics = canvas.getGraphicsContext2D(); - - this.size = size; - - this.canvas = canvas; - this.graphics = graphics; - - this.rows = rows; - this.columns = columns; - - this.gapSize = gapSize; - - cells = new Cell[rows * columns]; - - final float cellWidth = ((float)size - (rows - 1) * gapSize) / rows; - final float cellHeight = ((float)size - (columns - 1) * gapSize) / rows; - - for (int y = 0; y < columns; y++) { - final float startY = y * cellHeight + y * gapSize; - - for (int x = 0; x < rows; x++) { - final float startX = x * cellWidth + x * gapSize; - cells[y * rows + x] = new Cell(startX, startY, cellWidth, cellHeight); - } - } - - final Region background = createBackground(); - - final Text player1 = createText("player_1", "Player 1"); - final Text player2 = createText("player_2", "Player 2"); - - final HBox playersContainer = new HBox(100, player1, player2); - playersContainer.setAlignment(Pos.TOP_CENTER); - playersContainer.setPickOnBounds(false); - playersContainer.setTranslateY(50); - - final Button hint = createButton("Hint", () -> {}); - final Button back = createButton("Back", () -> { App.activate(new MainMenu()); }); - - final VBox controlContainer = new VBox(hint, back); - StackPane.setAlignment(controlContainer, Pos.BOTTOM_LEFT); - controlContainer.setPickOnBounds(false); - - pane = new StackPane(background, canvas, playersContainer, controlContainer); - } -} \ No newline at end of file diff --git a/app/src/main/java/org/toop/local/AppContext.java b/app/src/main/java/org/toop/local/AppContext.java index 3b1bb47..4a7ce7f 100644 --- a/app/src/main/java/org/toop/local/AppContext.java +++ b/app/src/main/java/org/toop/local/AppContext.java @@ -8,6 +8,7 @@ public class AppContext { public static void setCurrentLocale(Locale locale) { currentLocale = locale; } + public static Locale getLocale() { return currentLocale; }