From 3c699cde010de532c507ed5839208a09ae42eb13 Mon Sep 17 00:00:00 2001 From: ramollia <@> Date: Thu, 2 Oct 2025 19:59:14 +0200 Subject: [PATCH] merged --- app/src/main/java/org/toop/Main.java | 2 +- app/src/main/java/org/toop/app/App.java | 6 +- .../main/java/org/toop/app/menu/GameMenu.java | 22 --- .../org/toop/app/menu/GameSelectMenu.java | 33 +++++ .../main/java/org/toop/app/menu/MainMenu.java | 16 +- app/src/main/java/org/toop/app/menu/Menu.java | 8 - .../java/org/toop/app/menu/game/GameMenu.java | 99 +++++++++++++ .../org/toop/app/menu/game/TicTacToeMenu.java | 137 ++++++++++++++++++ .../main/java/org/toop/app/screen/Screen.java | 71 --------- .../org/toop/app/screen/TicTacToeScreen.java | 78 ---------- app/src/main/resources/assets/style/app.css | 2 +- game/src/main/java/org/toop/game/Game.java | 2 +- game/src/main/java/org/toop/game/Player.java | 2 +- .../org/toop/game/tictactoe/TicTacToe.java | 5 +- 14 files changed, 284 insertions(+), 199 deletions(-) delete mode 100644 app/src/main/java/org/toop/app/menu/GameMenu.java create mode 100644 app/src/main/java/org/toop/app/menu/GameSelectMenu.java create mode 100644 app/src/main/java/org/toop/app/menu/game/GameMenu.java create mode 100644 app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java delete mode 100644 app/src/main/java/org/toop/app/screen/Screen.java delete mode 100644 app/src/main/java/org/toop/app/screen/TicTacToeScreen.java diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java index 8a801ab..1c3e936 100644 --- a/app/src/main/java/org/toop/Main.java +++ b/app/src/main/java/org/toop/Main.java @@ -8,7 +8,7 @@ import org.toop.framework.networking.NetworkingClientManager; import org.toop.framework.networking.NetworkingInitializationException; public final class Main { - static void main(String[] args) { + public static void main(String[] args) { initSystems(); App.run(args); } diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 1b06df6..6c6c284 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -13,6 +13,8 @@ import javafx.scene.layout.StackPane; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.stage.Stage; +import org.toop.framework.asset.AssetManager; +import org.toop.framework.asset.resources.CssAsset; public final class App extends Application { private static Stage stage; @@ -42,7 +44,7 @@ public final class App extends Application { box.setMaxHeight(200); pane = new StackPane(background, box); - pane.getStylesheets().add(App.class.getResource("/style/quit.css").toExternalForm()); + pane.getStylesheets().add(((CssAsset)AssetManager.get("quit.css")).getUrl()); } } @@ -55,7 +57,7 @@ public final class App extends Application { final StackPane root = new StackPane(new MainMenu().getPane()); final Scene scene = new Scene(root); - scene.getStylesheets().add(App.class.getResource("/style/app.css").toExternalForm()); + scene.getStylesheets().add(((CssAsset)AssetManager.get("app.css")).getUrl()); stage.setTitle("pism"); stage.setMinWidth(1080); diff --git a/app/src/main/java/org/toop/app/menu/GameMenu.java b/app/src/main/java/org/toop/app/menu/GameMenu.java deleted file mode 100644 index 3bcc700..0000000 --- a/app/src/main/java/org/toop/app/menu/GameMenu.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.toop.app.menu; - -import javafx.scene.control.ComboBox; -import javafx.scene.control.TextField; -import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; -import javafx.scene.layout.VBox; -import org.toop.app.GameType; - -public class GameMenu extends Menu { - public GameMenu(GameType type) { - final Region background = createBackground(); - - ComboBox selectedGame = new ComboBox<>(); - - TextField serverIpField = new TextField(); - serverIpField.setPromptText("Enter here your server ip address"); - - VBox box = new VBox(selectedGame, serverIpField); - pane = new StackPane(background, box); - } -} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/menu/GameSelectMenu.java b/app/src/main/java/org/toop/app/menu/GameSelectMenu.java new file mode 100644 index 0000000..a205808 --- /dev/null +++ b/app/src/main/java/org/toop/app/menu/GameSelectMenu.java @@ -0,0 +1,33 @@ +package org.toop.app.menu; + +import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.StackPane; +import javafx.scene.layout.VBox; +import org.toop.app.GameType; + +public class GameSelectMenu extends Menu { + public GameSelectMenu(GameType type) { + final Region background = createBackground(); + + final ComboBox selectedGame = new ComboBox<>(); + selectedGame.getItems().add(GameType.toName(GameType.TICTACTOE)); + selectedGame.getItems().add(GameType.toName(GameType.REVERSI)); + selectedGame.setValue(GameType.toName(type)); + + final ComboBox selectedMode = new ComboBox<>(); + selectedMode.getItems().add("Local"); + selectedMode.getItems().add("Online"); + selectedMode.setValue("Local"); + + final HBox selectedContainer = new HBox(10, selectedGame, selectedMode); + + final TextField serverIpField = new TextField(); + serverIpField.setPromptText("Enter here your server ip address"); + + VBox box = new VBox(selectedContainer, serverIpField); + pane = new StackPane(background, box); + } +} \ 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 16e6a37..c369806 100644 --- a/app/src/main/java/org/toop/app/menu/MainMenu.java +++ b/app/src/main/java/org/toop/app/menu/MainMenu.java @@ -2,18 +2,19 @@ package org.toop.app.menu; import org.toop.app.App; import org.toop.app.GameType; -import org.toop.app.screen.TicTacToeScreen; 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 GameMenu(GameType.TICTACTOE)); }); - final Button reversi = createButton("Reversi", () -> { App.activate(new GameMenu(GameType.REVERSI)); }); + final Button tictactoe = createButton("Tic Tac Toe", () -> { App.activate(new TicTacToeMenu(new TicTacToe("player 1", true, "player 2", true))); }); + final Button reversi = createButton("Reversi", () -> { App.activate(new GameSelectMenu(GameType.REVERSI)); }); final VBox gamesBox = new VBox(10, tictactoe, reversi); gamesBox.setAlignment(Pos.TOP_LEFT); @@ -32,14 +33,5 @@ public final class MainMenu extends Menu { controlBox.setTranslateX(25); pane = new StackPane(background, gamesBox, controlBox); - - tictactoe.setOnMouseEntered(_ -> { - final TicTacToeScreen screen = new TicTacToeScreen((int)pane.getHeight()); - screen.simulate(1000); - - push(screen.getCanvas()); - }); - - tictactoe.setOnMouseExited(_ -> { pop(); }); } } \ No newline at end of file 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 a283ef4..82c12ee 100644 --- a/app/src/main/java/org/toop/app/menu/Menu.java +++ b/app/src/main/java/org/toop/app/menu/Menu.java @@ -10,14 +10,6 @@ public abstract class Menu { protected Pane pane; public Pane getPane() { return pane; } - public void push(Node node) { - pane.getChildren().addLast(node); - } - - public void pop() { - pane.getChildren().removeLast(); - } - public Region createBackground(String css) { final Region background = new Region(); background.setPrefSize(Double.MAX_VALUE, Double.MAX_VALUE); 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 new file mode 100644 index 0000000..4efa4ff --- /dev/null +++ b/app/src/main/java/org/toop/app/menu/game/GameMenu.java @@ -0,0 +1,99 @@ +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/app/menu/game/TicTacToeMenu.java b/app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java new file mode 100644 index 0000000..5b2421b --- /dev/null +++ b/app/src/main/java/org/toop/app/menu/game/TicTacToeMenu.java @@ -0,0 +1,137 @@ +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 java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; + +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); + } + } + }); + + this.executor.submit(this::gameThread); + } + + 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; + } + } + + executor.close(); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/screen/Screen.java b/app/src/main/java/org/toop/app/screen/Screen.java deleted file mode 100644 index 8bab32f..0000000 --- a/app/src/main/java/org/toop/app/screen/Screen.java +++ /dev/null @@ -1,71 +0,0 @@ -package org.toop.app.screen; - -import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; - -public abstract class Screen { - 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(int x, int y) { - return x >= this.x && y >= this.y && x <= this.x + width && y <= this.y + height; - } - } - - protected final Canvas canvas; - protected final GraphicsContext graphics; - - protected final int width; - protected final int height; - - protected final int rowSize; - protected final int columnSize; - - protected final int gapSize; - - protected final Cell[] cells; - - protected Screen(int width, int height, int rowSize, int columnSize, int gapSize) { - final Canvas canvas = new Canvas(width, height); - final GraphicsContext graphics = canvas.getGraphicsContext2D(); - - this.canvas = canvas; - this.graphics = graphics; - - this.width = width; - this.height = height; - - this.rowSize = rowSize; - this.columnSize = columnSize; - - this.gapSize = gapSize; - - cells = new Cell[rowSize * columnSize]; - - final float cellWidth = ((float)width - (rowSize - 1) * gapSize) / rowSize; - final float cellHeight = ((float)height - (columnSize - 1) * gapSize) / rowSize; - - for (int y = 0; y < columnSize; y++) { - final float startY = y * cellHeight + y * gapSize; - - for (int x = 0; x < rowSize; x++) { - final float startX = x * cellWidth + x * gapSize; - cells[y * rowSize + x] = new Cell(startX, startY, cellWidth, cellHeight); - } - } - } - - public Canvas getCanvas() { return canvas; } -} \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/screen/TicTacToeScreen.java b/app/src/main/java/org/toop/app/screen/TicTacToeScreen.java deleted file mode 100644 index 6a1e57d..0000000 --- a/app/src/main/java/org/toop/app/screen/TicTacToeScreen.java +++ /dev/null @@ -1,78 +0,0 @@ -package org.toop.app.screen; - -import javafx.animation.KeyFrame; -import javafx.animation.Timeline; -import javafx.scene.paint.Color; -import javafx.util.Duration; - -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - -public class TicTacToeScreen extends Screen { - public TicTacToeScreen(int size) { - super(size, size, 3, 3, 10); - - graphics.setFill(Color.CYAN); - - for (int x = 1; x < rowSize; x++) { - graphics.fillRect(cells[x].x - gapSize, 0, gapSize, height - gapSize); - } - - for (int y = 1; y < columnSize; y++) { - graphics.fillRect(0, cells[y * rowSize].y - gapSize, width - gapSize, gapSize); - } - } - - public void placeX(int cell) { - graphics.setFill(Color.WHITE); - graphics.setLineWidth(gapSize); - - final float x = cells[cell].x; - final float y = cells[cell].y; - - final float width = cells[cell].width; - final float height = cells[cell].height; - - graphics.strokeLine(x, y, x + width, y + height); - graphics.strokeLine(x + width, y, x, y + height); - } - - public void placeO(int cell) { - graphics.setFill(Color.WHITE); - graphics.setLineWidth(gapSize); - - final float x = cells[cell].x; - final float y = cells[cell].y; - - final float width = cells[cell].width; - final float height = cells[cell].height; - - graphics.strokeOval(x, y, width, height); - } - - public void simulate(int speedInMilliseconds) { - final Random random = new Random(); - - final List playedCells = new ArrayList<>(); - - final Timeline timeline = new Timeline(new KeyFrame(Duration.millis(speedInMilliseconds), _ ->{ - int cell; - - do { - cell = random.nextInt(cells.length); - } while (playedCells.contains(cell)); - - if (playedCells.size() % 2 == 0) { - placeX(cell); - } else { - placeO(cell); - } - - playedCells.add(cell); - })); - - timeline.setCycleCount(9); - timeline.play(); - } -} \ No newline at end of file diff --git a/app/src/main/resources/assets/style/app.css b/app/src/main/resources/assets/style/app.css index d31e450..de9330f 100644 --- a/app/src/main/resources/assets/style/app.css +++ b/app/src/main/resources/assets/style/app.css @@ -1,5 +1,5 @@ .background { - -fx-background-image: url("/image/lowpoly.png"); + -fx-background-color: linear-gradient(to bottom right, #21a7b2, #8f32b9); } .text { diff --git a/game/src/main/java/org/toop/game/Game.java b/game/src/main/java/org/toop/game/Game.java index b37bd73..a4220ed 100644 --- a/game/src/main/java/org/toop/game/Game.java +++ b/game/src/main/java/org/toop/game/Game.java @@ -4,7 +4,7 @@ import java.util.Arrays; public abstract class Game { public enum State { - NORMAL, LOSE, DRAW, WIN, + NORMAL, DRAW, LOSE, WIN, } public record Move(int position, char value) {} diff --git a/game/src/main/java/org/toop/game/Player.java b/game/src/main/java/org/toop/game/Player.java index 2dc4a2f..22e46c1 100644 --- a/game/src/main/java/org/toop/game/Player.java +++ b/game/src/main/java/org/toop/game/Player.java @@ -1,3 +1,3 @@ package org.toop.game; -public record Player(String name, char... values) {} \ No newline at end of file +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/tictactoe/TicTacToe.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java index cce644f..dd50eeb 100644 --- a/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java +++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToe.java @@ -8,8 +8,8 @@ import java.util.ArrayList; public final class TicTacToe extends Game { private int movesLeft; - public TicTacToe(String player1, String player2) { - super(3, 3, new Player(player1, 'X'), new Player(player2, 'O')); + public TicTacToe(String player1, boolean isPlayer1AI, String player2, boolean isPlayer2AI) { + super(3, 3, new Player(player1, isPlayer1AI, 'X'), new Player(player2, isPlayer2AI, 'O')); movesLeft = board.length; } @@ -51,6 +51,7 @@ public final class TicTacToe extends Game { return State.DRAW; } } + return State.NORMAL; }