diff --git a/app/src/main/java/org/toop/Main.java b/app/src/main/java/org/toop/Main.java index 3b4fef3..bd330d2 100644 --- a/app/src/main/java/org/toop/Main.java +++ b/app/src/main/java/org/toop/Main.java @@ -4,6 +4,50 @@ import org.toop.app.App; public final class Main { static void main(String[] args) { - App.run(args); + App.run(args); + // testMCTS(10); } + + // Voor onderzoek + // private static void testMCTS(int games) { + // var random = new ArtificialPlayer<>(new RandomAI(), "Random AI"); + // var v1 = new ArtificialPlayer<>(new MCTSAI(10), "MCTS V1 AI"); + // var v2 = new ArtificialPlayer<>(new MCTSAI2(10), "MCTS V2 AI"); + // var v2_2 = new ArtificialPlayer<>(new MCTSAI2(100), "MCTS V2_2 AI"); + // var v3 = new ArtificialPlayer<>(new MCTSAI3(10), "MCTS V3 AI"); + + // testAI(games, new Player[]{ v1, v2 }); + // // testAI(games, new Player[]{ v1, v3 }); + + // // testAI(games, new Player[]{ random, v3 }); + // // testAI(games, new Player[]{ v2, v3 }); + // testAI(games, new Player[]{ v2, v3 }); + // // testAI(games, new Player[]{ v3, v2 }); + // } + + // private static void testAI(int games, Player[] ais) { + // int wins = 0; + // int ties = 0; + + // for (int i = 0; i < games; i++) { + // final BitboardReversi match = new BitboardReversi(ais); + + // while (!match.isTerminal()) { + // final int currentAI = match.getCurrentTurn(); + // final long move = ais[currentAI].getMove(match); + + // match.play(move); + // } + + // if (match.getWinner() < 0) { + // ties++; + // continue; + // } + + // wins += match.getWinner() == 0? 1 : 0; + // } + + // System.out.printf("Out of %d games, %s won %d -- tied %d -- lost %d, games against %s\n", games, ais[0].getName(), wins, ties, games - wins - ties, ais[1].getName()); + // System.out.printf("Average win rate was: %.2f\n\n", wins / (float)games); + // } } diff --git a/app/src/main/java/org/toop/app/App.java b/app/src/main/java/org/toop/app/App.java index 5d99acd..9645cd5 100644 --- a/app/src/main/java/org/toop/app/App.java +++ b/app/src/main/java/org/toop/app/App.java @@ -1,10 +1,14 @@ package org.toop.app; +import javafx.application.Application; import javafx.application.Platform; +import javafx.geometry.Pos; +import javafx.scene.Scene; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyEvent; - +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; import org.toop.app.widget.Primitive; import org.toop.app.widget.WidgetContainer; import org.toop.app.widget.complex.LoadingWidget; @@ -16,8 +20,8 @@ import org.toop.framework.audio.*; import org.toop.framework.audio.events.AudioEvents; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; -import org.toop.framework.networking.NetworkingClientEventListener; -import org.toop.framework.networking.NetworkingClientManager; +import org.toop.framework.networking.connection.NetworkingClientEventListener; +import org.toop.framework.networking.connection.NetworkingClientManager; import org.toop.framework.resource.ResourceLoader; import org.toop.framework.resource.ResourceManager; import org.toop.framework.resource.events.AssetLoaderEvents; @@ -27,12 +31,6 @@ import org.toop.framework.resource.resources.SoundEffectAsset; import org.toop.local.AppContext; import org.toop.local.AppSettings; -import javafx.application.Application; -import javafx.geometry.Pos; -import javafx.scene.Scene; -import javafx.scene.layout.StackPane; -import javafx.stage.Stage; - import java.util.Objects; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; @@ -86,7 +84,7 @@ public final class App extends Application { AppSettings.applySettings(); - setKeybinds(root); + setKeybinds(root); LoadingWidget loading = new LoadingWidget(Primitive.text( "Loading...", false), 0, 0, Integer.MAX_VALUE, false, false // Just set a high default @@ -94,7 +92,7 @@ public final class App extends Application { WidgetContainer.setCurrentView(loading); - setOnLoadingSuccess(loading); + setOnLoadingSuccess(loading); EventFlow loadingFlow = new EventFlow(); @@ -137,13 +135,12 @@ public final class App extends Application { } stage.show(); - - } + } private void setKeybinds(StackPane root) { root.addEventHandler(KeyEvent.KEY_PRESSED,event -> { if (event.getCode() == KeyCode.ESCAPE) { - escapePopup(); + escapePopup(); } }); stage.setFullScreenExitKeyCombination( diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index ef691b4..f6bcdec 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -11,17 +11,15 @@ import org.toop.app.widget.popup.ErrorPopup; import org.toop.app.widget.popup.SendChallengePopup; import org.toop.app.widget.view.ServerView; import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.game.players.OnlinePlayer; import org.toop.framework.gameFramework.controller.GameController; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.framework.networking.clients.TournamentNetworkingClient; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.framework.networking.types.NetworkingConnector; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.ArtificialPlayer; -import org.toop.game.players.OnlinePlayer; -import org.toop.game.players.RandomAI; +import org.toop.framework.networking.connection.clients.TournamentNetworkingClient; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.networking.connection.types.NetworkingConnector; +import org.toop.framework.networking.server.gateway.NettyGatewayServer; +import org.toop.framework.game.players.LocalPlayer; import org.toop.local.AppContext; import java.util.List; @@ -32,7 +30,8 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; public final class Server { - // TODO: Keep track of listeners. Remove them on Server connection close so reference is deleted. + private NettyGatewayServer nettyGatewayServer; + private String user = ""; private long clientId = -1; @@ -60,10 +59,13 @@ public final class Server { return null; } + public Server(String ip, String port, String user) { + this(ip, port, user, null); + } // Server has to deal with ALL network related listen events. This "server" can then interact with the manager to make stuff happen. // This prevents data races where events get sent to the game manager but the manager isn't ready yet. - public Server(String ip, String port, String user) { + public Server(String ip, String port, String user, NettyGatewayServer nettyGatewayServer) { if (ip.split("\\.").length < 4) { new ErrorPopup("\"" + ip + "\" " + AppContext.getString("is-not-a-valid-ip-address")); return; @@ -83,6 +85,8 @@ public final class Server { return; } + this.nettyGatewayServer = nettyGatewayServer; + final int reconnectAttempts = 10; LoadingWidget loading = new LoadingWidget( @@ -113,7 +117,7 @@ public final class Server { return; } - primary = new ServerView(user, this::sendChallenge); + primary = new ServerView(user, this::sendChallenge, clientId); WidgetContainer.getCurrentView().transitionNextCustom(primary, "disconnect", this::disconnect); a.unsubscribe("connecting"); @@ -154,7 +158,8 @@ public final class Server { .listen(NetworkEvents.GameMatchResponse.class, this::handleMatchResponse, false, "match-response") .listen(NetworkEvents.GameResultResponse.class, this::handleGameResult, false, "game-result") .listen(NetworkEvents.GameMoveResponse.class, this::handleReceivedMove, false, "game-move") - .listen(NetworkEvents.YourTurnResponse.class, this::handleYourTurn, false, "your-turn"); + .listen(NetworkEvents.YourTurnResponse.class, this::handleYourTurn, false, "your-turn") + .listen(NetworkEvents.ClosedConnection.class, this::closedConnection, false, "closed-connection"); connectFlow = a; } @@ -178,7 +183,7 @@ public final class Server { gameController = null; - //if (!isPolling) return; +// if (!isPolling) return; String gameType = extractQuotedValue(response.gameType()); if (response.clientId() == clientId) { @@ -191,45 +196,29 @@ public final class Server { return; } - final int myTurn = response.playerToMove().equalsIgnoreCase(response.opponent()) ? 1 : 0; + final String startingPlayer = response.playerToMove(); + final int userStartingTurn = startingPlayer.equalsIgnoreCase(user) ? 0 : 1; + final int opponentStartingTurn = 1 - userStartingTurn; final GameInformation information = new GameInformation(type); - //information.players[0] = playerInformation; - information.players[0].name = user; - information.players[0].isHuman = false; - information.players[0].computerDifficulty = 5; - information.players[0].computerThinkTime = 1; - information.players[1].name = response.opponent(); - - /*switch (type){ - case TICTACTOE ->{ - players[myTurn] = new ArtificialPlayer<>(new TicTacToeAIR(9), user); - } - case REVERSI ->{ - players[myTurn] = new ArtificialPlayer<>(new ReversiAIR(), user); - } - }*/ - + information.players[userStartingTurn].name = user; + information.players[opponentStartingTurn].name = response.opponent(); + Player[] players = new Player[2]; + players[userStartingTurn] = new LocalPlayer(user); + players[opponentStartingTurn] = new OnlinePlayer(response.opponent()); switch (type) { - case TICTACTOE ->{ - Player[] players = new Player[2]; - players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); - players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); - gameController = new TicTacToeBitController(players); - } - case REVERSI -> { - Player[] players = new Player[2]; - players[(myTurn + 1) % 2] = new OnlinePlayer<>(response.opponent()); - players[myTurn] = new ArtificialPlayer<>(new RandomAI(), user); - gameController = new ReversiBitController(players);} + case TICTACTOE -> gameController = new TicTacToeBitController(players); + case REVERSI -> gameController = new ReversiBitController(players); default -> new ErrorPopup("Unsupported game type."); } - if (gameController != null){ + if (gameController != null) { + primary.reEnableButton(); // Re enable subscribe button gameController.start(); + isPolling = true; // Fixes server getting stuck } } } @@ -263,7 +252,7 @@ public final class Server { String gameType = extractQuotedValue(response.gameType()); final String finalGameType = gameType; var a = new ChallengePopup(challengerName, gameType, (playerInformation) -> { - final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); + final long challengeId = Long.parseLong(response.challengeId().replaceAll("\\D", "")); new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); isSingleGame.set(true); }); @@ -281,9 +270,27 @@ public final class Server { stopScheduler(); connectFlow.unsubscribeAll(); + if (nettyGatewayServer != null) { + nettyGatewayServer.stop(); + } + WidgetContainer.getCurrentView().transitionPrevious(); } + private void closedConnection(NetworkEvents.ClosedConnection e) { + new EventFlow().addPostEvent(new NetworkEvents.CloseClient(clientId)).postEvent(); + isPolling = false; + stopScheduler(); + connectFlow.unsubscribeAll(); + + if (nettyGatewayServer != null) { + nettyGatewayServer.stop(); + } + + WidgetContainer.getCurrentView().transitionPrevious(); + WidgetContainer.add(Pos.CENTER, new ErrorPopup("Server closed connection.")); + } + private void forfeitGame() { new EventFlow().addPostEvent(new NetworkEvents.SendForfeit(clientId)).postEvent(); } @@ -330,7 +337,9 @@ public final class Server { private void gamesListFromServerHandler(NetworkEvents.GamelistResponse event) { gameList.clear(); - gameList.addAll(List.of(event.gamelist())); + var gl = List.of(event.gamelist()); + gameList.addAll(gl); + primary.updateGameList(gl); } public void populateGameList() { diff --git a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java index 85c6f7d..b441f44 100644 --- a/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/BitGameCanvas.java @@ -14,7 +14,7 @@ import org.toop.framework.gameFramework.view.GUIEvents; import java.util.function.Consumer; -public abstract class BitGameCanvas> implements GameCanvas { +public abstract class BitGameCanvas implements GameCanvas { protected record Cell(float x, float y, float width, float height) { public boolean isInside(double x, double y) { return x >= this.x && x <= this.x + width && @@ -78,6 +78,7 @@ public abstract class BitGameCanvas> implements GameC } canvas.setOnMouseClicked(event -> { + if (event.getButton() != MouseButton.PRIMARY) { return; } @@ -93,9 +94,6 @@ public abstract class BitGameCanvas> implements GameC } }); - - - render(); } diff --git a/app/src/main/java/org/toop/app/canvas/GameCanvas.java b/app/src/main/java/org/toop/app/canvas/GameCanvas.java index d1361c5..e3826df 100644 --- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -3,6 +3,7 @@ package org.toop.app.canvas; import javafx.scene.canvas.Canvas; import org.toop.framework.gameFramework.model.game.TurnBasedGame; -public interface GameCanvas> extends GameDrawer{ +public interface GameCanvas { Canvas getCanvas(); + void redraw(TurnBasedGame gameCopy); } diff --git a/app/src/main/java/org/toop/app/canvas/GameDrawer.java b/app/src/main/java/org/toop/app/canvas/GameDrawer.java deleted file mode 100644 index 261334c..0000000 --- a/app/src/main/java/org/toop/app/canvas/GameDrawer.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.toop.app.canvas; - -import org.toop.framework.gameFramework.model.game.TurnBasedGame; - -public interface GameDrawer> { - void redraw(T gameCopy); -} diff --git a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java index 7c2bde0..685a985 100644 --- a/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/ReversiBitCanvas.java @@ -2,12 +2,9 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; -import org.toop.game.games.reversi.BitboardReversi; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import java.util.Arrays; -import java.util.function.Consumer; - -public class ReversiBitCanvas extends BitGameCanvas { +public class ReversiBitCanvas extends BitGameCanvas { public ReversiBitCanvas() { super(Color.GRAY, new Color(0f, 0.4f, 0.2f, 1f), (App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3, 8, 8, 5, true); canvas.setOnMouseMoved(event -> { @@ -33,7 +30,7 @@ public class ReversiBitCanvas extends BitGameCanvas { } @Override - public void redraw(BitboardReversi gameCopy) { + public void redraw(TurnBasedGame gameCopy) { clearAll(); long[] board = gameCopy.getBoard(); loopOverBoard(board[0], (i) -> drawDot(Color.WHITE, i)); diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java index 443adbd..7c9b5b4 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeBitCanvas.java @@ -2,12 +2,9 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.app.App; -import org.toop.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; -import java.util.Arrays; -import java.util.function.Consumer; - -public class TicTacToeBitCanvas extends BitGameCanvas{ +public class TicTacToeBitCanvas extends BitGameCanvas{ public TicTacToeBitCanvas() { super( Color.GRAY, @@ -22,7 +19,7 @@ public class TicTacToeBitCanvas extends BitGameCanvas{ } @Override - public void redraw(BitboardTicTacToe gameCopy) { + public void redraw(TurnBasedGame gameCopy) { clearAll(); drawMoves(gameCopy.getBoard()); } diff --git a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java index 2c3ad49..0c68a74 100644 --- a/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/GenericGameController.java @@ -10,15 +10,15 @@ import org.toop.app.widget.view.GameView; import org.toop.framework.eventbus.EventFlow; import org.toop.framework.eventbus.GlobalEventBus; import org.toop.framework.gameFramework.controller.GameController; -import org.toop.framework.gameFramework.model.game.SupportsOnlinePlay; +import org.toop.framework.gameFramework.model.game.threadBehaviour.SupportsOnlinePlay; import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; import org.toop.framework.gameFramework.view.GUIEvents; -import org.toop.framework.networking.events.NetworkEvents; -import org.toop.game.players.LocalPlayer; +import org.toop.framework.networking.connection.events.NetworkEvents; +import org.toop.framework.game.players.LocalPlayer; -public class GenericGameController> implements GameController { +public class GenericGameController implements GameController { protected final EventFlow eventFlow = new EventFlow(); // Logger for logging @@ -28,21 +28,23 @@ public class GenericGameController> implements GameCo protected final GameView gameView; // Reference to game canvas - protected final GameCanvas canvas; + protected final GameCanvas canvas; - protected final TurnBasedGame game; // Reference to game instance + protected final TurnBasedGame game; // Reference to game instance private final ThreadBehaviour gameThreadBehaviour; // TODO: Change gameType to automatically happen with either dependency injection or something else. - public GenericGameController(GameCanvas canvas, T game, ThreadBehaviour gameThreadBehaviour, String gameType) { - logger.info("Creating: " + this.getClass()); + public GenericGameController(GameCanvas canvas, TurnBasedGame game, ThreadBehaviour gameThreadBehaviour, String gameType) { + logger.info("Creating: {}", this.getClass()); this.canvas = canvas; this.game = game; this.gameThreadBehaviour = gameThreadBehaviour; // Tell thread how to send moves - this.gameThreadBehaviour.setOnSendMove((id, m) -> GlobalEventBus.get().post(new NetworkEvents.SendMove(id, (short)translateMove(m)))); + this.gameThreadBehaviour.setOnSendMove( + (id, m) -> GlobalEventBus.get().post(new NetworkEvents.SendMove(id, (short)translateMove(m))) + ); // Tell thread how to update UI this.gameThreadBehaviour.setOnUpdateUI(() -> Platform.runLater(this::updateUI)); @@ -53,24 +55,40 @@ public class GenericGameController> implements GameCo WidgetContainer.getCurrentView().transitionNext(gameView, true); // Listen to updates + logger.info("Game controller started listening"); eventFlow .listen(GUIEvents.GameEnded.class, this::onGameFinish, false) - .listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());}}, false); + .listen(GUIEvents.PlayerAttemptedMove.class, event -> { + logger.info("User attempting move {}", event.move()); + logger.info("Current player's turn {}", getCurrentPlayer().getName()); + logger.info("First player {}", game.getPlayer(0).getName()); + logger.info("Username {}", getCurrentPlayer().getName()); + logger.info("User is class {}, {}", getCurrentPlayer().getClass(), getCurrentPlayer() instanceof LocalPlayer); + if (getCurrentPlayer() instanceof LocalPlayer lp) { + try { + lp.setLastMove(event.move()); + } catch (Exception e) { + IO.println(e); + } + } + }, false); } public void start(){ logger.info("Starting GameManager"); updateUI(); gameThreadBehaviour.start(); + logger.info("GameManager started"); } public void stop(){ logger.info("Stopping GameManager"); removeListeners(); gameThreadBehaviour.stop(); + logger.info("GameManager stopped"); } - public Player getCurrentPlayer(){ + public Player getCurrentPlayer(){ return game.getPlayer(getCurrentPlayerIndex()); } @@ -91,14 +109,14 @@ public class GenericGameController> implements GameCo } private void onGameFinish(GUIEvents.GameEnded event){ - logger.info("Game Finished"); + logger.info("OnlineTurnBasedGame Finished"); String name = event.winner() == -1 ? null : getPlayer(event.winner()).getName(); gameView.gameOver(event.winOrTie(), name); stop(); } - public Player getPlayer(int player){ - if (player < 0 || player >= 2){ // TODO: Make game turn player count + public Player getPlayer(int player){ + if (player < 0 || player > game.getPlayerCount()-1){ // TODO: Make game turn player count logger.error("Invalid player index"); throw new IllegalArgumentException("player out of range"); } diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java index 40784b0..1ea8109 100644 --- a/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiBitController.java @@ -3,20 +3,21 @@ package org.toop.app.gameControllers; import org.toop.app.canvas.ReversiBitCanvas; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.gameThreads.LocalThreadBehaviour; -import org.toop.game.gameThreads.OnlineThreadBehaviour; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.players.OnlinePlayer; +import org.toop.framework.game.gameThreads.LocalThreadBehaviour; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.players.OnlinePlayer; + +import java.util.Arrays; + +public class ReversiBitController extends GenericGameController { + public ReversiBitController(Player[] players) { + BitboardReversi game = new BitboardReversi(); + game.init(players); + + ThreadBehaviour thread = Arrays.stream(players).anyMatch(e -> e instanceof OnlinePlayer) ? + new OnlineThreadBehaviour(game) : new LocalThreadBehaviour(game); -public class ReversiBitController extends GenericGameController { - public ReversiBitController(Player[] players) { - BitboardReversi game = new BitboardReversi(players); - ThreadBehaviour thread = new LocalThreadBehaviour<>(game); - for (Player player : players) { - if (player instanceof OnlinePlayer){ - thread = new OnlineThreadBehaviour<>(game); - } - } super(new ReversiBitCanvas(), game, thread, "Reversi"); } } diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java index 6307894..8315249 100644 --- a/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeBitController.java @@ -3,21 +3,21 @@ package org.toop.app.gameControllers; import org.toop.app.canvas.TicTacToeBitCanvas; import org.toop.framework.gameFramework.model.game.threadBehaviour.ThreadBehaviour; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.gameThreads.LocalFixedRateThreadBehaviour; -import org.toop.game.gameThreads.LocalThreadBehaviour; -import org.toop.game.gameThreads.OnlineThreadBehaviour; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.OnlinePlayer; +import org.toop.framework.game.gameThreads.LocalThreadBehaviour; +import org.toop.framework.game.gameThreads.OnlineThreadBehaviour; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.game.players.OnlinePlayer; -public class TicTacToeBitController extends GenericGameController { - public TicTacToeBitController(Player[] players) { - BitboardTicTacToe game = new BitboardTicTacToe(players); - ThreadBehaviour thread = new LocalThreadBehaviour<>(game); - for (Player player : players) { - if (player instanceof OnlinePlayer){ - thread = new OnlineThreadBehaviour<>(game); - } - } - super(new TicTacToeBitCanvas(), game, thread , "TicTacToe"); +import java.util.Arrays; + +public class TicTacToeBitController extends GenericGameController { + public TicTacToeBitController(Player[] players) { + BitboardTicTacToe game = new BitboardTicTacToe(); + game.init(players); + + ThreadBehaviour thread = Arrays.stream(players).anyMatch(e -> e instanceof OnlinePlayer) ? + new OnlineThreadBehaviour(game) : new LocalThreadBehaviour(game); + + super(new TicTacToeBitCanvas(), game, thread, "TicTacToe"); } } diff --git a/app/src/main/java/org/toop/app/widget/Primitive.java b/app/src/main/java/org/toop/app/widget/Primitive.java index afb59a2..a8436c5 100644 --- a/app/src/main/java/org/toop/app/widget/Primitive.java +++ b/app/src/main/java/org/toop/app/widget/Primitive.java @@ -64,7 +64,7 @@ public final class Primitive { return imageView; } - public static Button button(String key, Runnable onAction, boolean localize) { + public static Button button(String key, Runnable onAction, boolean localize, boolean disableOnClick) { var button = new Button(); button.getStyleClass().add("button"); @@ -75,6 +75,7 @@ public final class Primitive { if (onAction != null) { button.setOnAction(_ -> { + if (disableOnClick) button.setDisable(true); onAction.run(); playButtonSound(); }); @@ -83,8 +84,8 @@ public final class Primitive { return button; } - public static Button button(String key, Runnable onAction) { - return button(key, onAction, true); + public static Button button(String key, Runnable onAction, boolean disableOnClick) { + return button(key, onAction, true, disableOnClick); } public static TextField input(String promptKey, String text, Consumer onValueChanged, boolean localize) { diff --git a/app/src/main/java/org/toop/app/widget/complex/ConfirmWidget.java b/app/src/main/java/org/toop/app/widget/complex/ConfirmWidget.java index e8719ce..1d6251d 100644 --- a/app/src/main/java/org/toop/app/widget/complex/ConfirmWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/ConfirmWidget.java @@ -26,7 +26,7 @@ public class ConfirmWidget implements Widget { public void addButton(String key, Runnable onClick) { Platform.runLater(() -> { - var button = Primitive.button(key, onClick); + var button = Primitive.button(key, onClick, false); buttonsContainer.getChildren().add(button); }); } 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 97a1140..fe1cf52 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 @@ -91,7 +91,7 @@ public class PlayerInfoWidget { information.computerDifficulty = depth; information.computerThinkTime = thinktime; this.playerName.setText(getName(name)); - }); + }, false); } private String getName(String name) { diff --git a/app/src/main/java/org/toop/app/widget/complex/TableWidget.java b/app/src/main/java/org/toop/app/widget/complex/TableWidget.java new file mode 100644 index 0000000..8c0c2ae --- /dev/null +++ b/app/src/main/java/org/toop/app/widget/complex/TableWidget.java @@ -0,0 +1,61 @@ +package org.toop.app.widget.complex; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Pos; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; + +import java.util.ArrayList; +import java.util.List; + +public class TableWidget extends PopupWidget { + private ObservableList serverList = FXCollections.observableArrayList(); + private TableView table = new TableView<>(); + + + public TableWidget(String... columns) { + var cols = new ArrayList>(); + + for (String column : columns) { + TableColumn col = new TableColumn<>(column.toUpperCase()); + col.setCellValueFactory(new PropertyValueFactory<>(column)); + cols.add(col); + } + + table.getColumns().addAll(cols); + update(); + onColumnClicked(); + + add(Pos.CENTER, table); + } + + public void add(DATATYPE serverFound) { + serverList.add(serverFound); + update(); + } + + public void add(List serverFound) { + serverList.addAll(serverFound); + } + + public void remove(DATATYPE serverFound) { + serverList.remove(serverFound); + update(); + } + + public void onColumnClicked() { + table.setOnMouseClicked(event -> { + DATATYPE selected = table.getSelectionModel().getSelectedItem(); + if (selected == null) return; + IO.println(selected.toString()); + }); + } + + private void update() { + table.setItems(serverList); + } + + +} diff --git a/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java b/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java index 217ac86..c280b1c 100644 --- a/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java +++ b/app/src/main/java/org/toop/app/widget/complex/ViewWidget.java @@ -32,7 +32,7 @@ public abstract class ViewWidget extends StackWidget { var backButton = Primitive.button("back", () -> { view.transitionPrevious(); - }); + }, false); view.add(Pos.BOTTOM_LEFT, Primitive.vbox(backButton)); } @@ -45,7 +45,7 @@ public abstract class ViewWidget extends StackWidget { var customButton = Primitive.button(key, () -> { runnable.run(); view.transitionPrevious(); - }); + }, false); view.add(Pos.BOTTOM_LEFT, Primitive.vbox(customButton)); } @@ -97,7 +97,7 @@ public abstract class ViewWidget extends StackWidget { var backButton = Primitive.button("back", () -> { view.transitionPrevious(); - }); + }, false); view.add(Pos.BOTTOM_LEFT, Primitive.vbox(backButton)); } diff --git a/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java b/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java index cd4d87c..e20730c 100644 --- a/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/ChallengePopup.java @@ -37,8 +37,8 @@ public final class ChallengePopup extends PopupWidget { var acceptButton = Primitive.button("accept", () -> { onAccept.accept(playerInformation); this.hide(); - }); - var denyButton = Primitive.button("deny", () -> hide()); + }, false); + var denyButton = Primitive.button("deny", () -> hide(), false); var leftSection = Primitive.vbox( challengeText, diff --git a/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java b/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java index d742ab6..5b4acd5 100644 --- a/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/EscapePopup.java @@ -18,13 +18,13 @@ public class EscapePopup extends PopupWidget { ViewWidget currentView = WidgetContainer.getCurrentView(); ArrayList nodes = new ArrayList<>(); - nodes.add(Primitive.button("Continue", this::hide, false)); // TODO, localize + nodes.add(Primitive.button("Continue", this::hide, false, false)); // TODO, localize if (!(currentView.getClass().isAssignableFrom(OptionsView.class))) { var opt = Primitive.button("options", () -> { hide(); WidgetContainer.getCurrentView().transitionNext(new OptionsView()); - }); + }, false); nodes.add(opt); } @@ -33,14 +33,14 @@ public class EscapePopup extends PopupWidget { if (tut != null) { nodes.add(Primitive.button("tutorialstring", () -> { WidgetContainer.getCurrentView().add(Pos.CENTER, tut); - })); + }, false)); } } nodes.add(Primitive.button("quit", () -> { hide(); WidgetContainer.add(Pos.CENTER, new QuitPopup()); - })); + }, false)); add(Pos.CENTER, Primitive.vbox(nodes.toArray(new Node[0]))); diff --git a/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java b/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java index bc42486..c5bfa7d 100644 --- a/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java +++ b/app/src/main/java/org/toop/app/widget/popup/SendChallengePopup.java @@ -62,9 +62,9 @@ public final class SendChallengePopup extends PopupWidget { var sendButton = Primitive.button( "send", () -> { onSend.accept(playerInformation, gameChoice.getValue()); this.hide(); } - ); + , false); - var cancelButton = Primitive.button("cancel", () -> hide()); + var cancelButton = Primitive.button("cancel", () -> hide(), false); var leftSection = Primitive.vbox( challengeText, diff --git a/app/src/main/java/org/toop/app/widget/tutorial/BaseTutorialWidget.java b/app/src/main/java/org/toop/app/widget/tutorial/BaseTutorialWidget.java index 54f40b0..02c872d 100644 --- a/app/src/main/java/org/toop/app/widget/tutorial/BaseTutorialWidget.java +++ b/app/src/main/java/org/toop/app/widget/tutorial/BaseTutorialWidget.java @@ -47,8 +47,8 @@ public class BaseTutorialWidget extends PopupWidget implements Updatable { this.pages = pages; this.nextScreen = nextScreen; - previousButton = Primitive.button("goback", () -> { update(false); this.hide(); }); - nextButton = Primitive.button(">", () -> update(true)); + previousButton = Primitive.button("goback", () -> { update(false); this.hide(); }, false); + nextButton = Primitive.button(">", () -> update(true), false); var w = Primitive.hbox( previousButton, diff --git a/app/src/main/java/org/toop/app/widget/tutorial/ShowEnableTutorialWidget.java b/app/src/main/java/org/toop/app/widget/tutorial/ShowEnableTutorialWidget.java index 62fa0c7..168225e 100644 --- a/app/src/main/java/org/toop/app/widget/tutorial/ShowEnableTutorialWidget.java +++ b/app/src/main/java/org/toop/app/widget/tutorial/ShowEnableTutorialWidget.java @@ -10,9 +10,9 @@ public class ShowEnableTutorialWidget extends PopupWidget { public ShowEnableTutorialWidget(Runnable tutorial, Runnable nextScreen, Runnable appSettingsSetter) { var a = Primitive.hbox( - Primitive.button("ok", () -> { appSettingsSetter.run(); tutorial.run(); this.hide(); }), - Primitive.button("no", () -> { appSettingsSetter.run(); nextScreen.run(); this.hide(); }), - Primitive.button("never", () -> { AppSettings.getSettings().setTutorialFlag(false); nextScreen.run(); this.hide(); }) + Primitive.button("ok", () -> { appSettingsSetter.run(); tutorial.run(); this.hide(); }, false), + Primitive.button("no", () -> { appSettingsSetter.run(); nextScreen.run(); this.hide(); }, false), + Primitive.button("never", () -> { AppSettings.getSettings().setTutorialFlag(false); nextScreen.run(); this.hide(); }, false) ); var txt = Primitive.text("tutorial"); diff --git a/app/src/main/java/org/toop/app/widget/view/GameView.java b/app/src/main/java/org/toop/app/widget/view/GameView.java index 94d8cd7..441cc0e 100644 --- a/app/src/main/java/org/toop/app/widget/view/GameView.java +++ b/app/src/main/java/org/toop/app/widget/view/GameView.java @@ -40,7 +40,7 @@ public final class GameView extends ViewWidget { player2Icon = new Circle(); if (onForfeit != null) { - forfeitButton = Primitive.button("forfeit", () -> onForfeit.run()); + forfeitButton = Primitive.button("forfeit", () -> onForfeit.run(), false); } else { forfeitButton = null; } @@ -48,7 +48,7 @@ public final class GameView extends ViewWidget { exitButton = Primitive.button("exit", () -> { onExit.run(); transitionPrevious(); - }); + }, false); if (onMessage != null) { chatInput = Primitive.input("enter-your-message", "", null); 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 9b9bed2..9139720 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 @@ -2,24 +2,21 @@ package org.toop.app.widget.view; import javafx.application.Platform; import org.toop.app.GameInformation; -import org.toop.app.canvas.ReversiBitCanvas; -import org.toop.app.canvas.TicTacToeBitCanvas; -import org.toop.app.gameControllers.GenericGameController; import org.toop.app.gameControllers.ReversiBitController; import org.toop.app.gameControllers.TicTacToeBitController; import org.toop.framework.gameFramework.controller.GameController; import org.toop.framework.gameFramework.model.player.Player; -import org.toop.game.games.reversi.BitboardReversi; -import org.toop.game.games.tictactoe.BitboardTicTacToe; -import org.toop.game.players.ArtificialPlayer; -import org.toop.game.players.LocalPlayer; +import org.toop.framework.game.players.ArtificialPlayer; import org.toop.app.widget.Primitive; import org.toop.app.widget.complex.PlayerInfoWidget; import org.toop.app.widget.complex.ViewWidget; import org.toop.app.widget.popup.ErrorPopup; import org.toop.app.widget.tutorial.*; -import org.toop.game.players.MiniMaxAI; -import org.toop.game.players.RandomAI; +import org.toop.framework.game.players.LocalPlayer; +import org.toop.game.players.ai.MCTSAI; +import org.toop.game.players.ai.MCTSAI2; +import org.toop.game.players.ai.MCTSAI3; +import org.toop.game.players.ai.MiniMaxAI; import org.toop.local.AppContext; import javafx.geometry.Pos; @@ -27,9 +24,6 @@ import javafx.scene.control.ScrollPane; import javafx.scene.layout.VBox; import org.toop.local.AppSettings; -import java.util.Arrays; -import java.util.Random; - public class LocalMultiplayerView extends ViewWidget { private final GameInformation information; @@ -58,14 +52,14 @@ public class LocalMultiplayerView extends ViewWidget { switch (information.type) { case TICTACTOE: if (information.players[0].isHuman) { - players[0] = new LocalPlayer<>(information.players[0].name); + players[0] = new LocalPlayer(information.players[0].name); } else { - players[0] = new ArtificialPlayer<>(new RandomAI(), "Random AI"); + players[0] = new ArtificialPlayer(new MCTSAI(100), "MCTS AI"); } if (information.players[1].isHuman) { - players[1] = new LocalPlayer<>(information.players[1].name); + players[1] = new LocalPlayer(information.players[1].name); } else { - players[1] = new ArtificialPlayer<>(new MiniMaxAI(9), "MiniMax AI"); + players[1] = new ArtificialPlayer(new MiniMaxAI(9), "MiniMax AI"); } if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstTTT()) { new ShowEnableTutorialWidget( @@ -86,14 +80,15 @@ public class LocalMultiplayerView extends ViewWidget { break; case REVERSI: if (information.players[0].isHuman) { - players[0] = new LocalPlayer<>(information.players[0].name); + players[0] = new LocalPlayer(information.players[0].name); } else { - players[0] = new ArtificialPlayer<>(new RandomAI(), "Random AI"); + // players[0] = new ArtificialPlayer(new RandomAI(), "Random AI"); + players[0] = new ArtificialPlayer(new MCTSAI3(50), "MCTS V3 AI"); } if (information.players[1].isHuman) { - players[1] = new LocalPlayer<>(information.players[1].name); + players[1] = new LocalPlayer(information.players[1].name); } else { - players[1] = new ArtificialPlayer<>(new MiniMaxAI(6), "MiniMax"); + players[1] = new ArtificialPlayer(new MCTSAI2(50), "MCTS V2 AI"); } if (AppSettings.getSettings().getTutorialFlag() && AppSettings.getSettings().getFirstReversi()) { new ShowEnableTutorialWidget( @@ -113,7 +108,7 @@ public class LocalMultiplayerView extends ViewWidget { } break; } - }); + }, false); var playerSection = setupPlayerSections(); diff --git a/app/src/main/java/org/toop/app/widget/view/LocalView.java b/app/src/main/java/org/toop/app/widget/view/LocalView.java index f002bf8..51fdb7e 100644 --- a/app/src/main/java/org/toop/app/widget/view/LocalView.java +++ b/app/src/main/java/org/toop/app/widget/view/LocalView.java @@ -10,11 +10,11 @@ public class LocalView extends ViewWidget { public LocalView() { var ticTacToeButton = Primitive.button("tic-tac-toe", () -> { transitionNext(new LocalMultiplayerView(GameInformation.Type.TICTACTOE)); - }); + }, false); var reversiButton = Primitive.button("reversi", () -> { transitionNext(new LocalMultiplayerView(GameInformation.Type.REVERSI)); - }); + }, false); add(Pos.CENTER, Primitive.vbox( ticTacToeButton, diff --git a/app/src/main/java/org/toop/app/widget/view/MainView.java b/app/src/main/java/org/toop/app/widget/view/MainView.java index a60d753..7dc8aed 100644 --- a/app/src/main/java/org/toop/app/widget/view/MainView.java +++ b/app/src/main/java/org/toop/app/widget/view/MainView.java @@ -9,24 +9,24 @@ public class MainView extends ViewWidget { public MainView() { var localButton = Primitive.button("local", () -> { transitionNext(new LocalView()); - }); + }, false); var onlineButton = Primitive.button("online", () -> { transitionNext(new OnlineView()); - }); + }, false); var creditsButton = Primitive.button("credits", () -> { transitionNext(new CreditsView()); - }); + }, false); var optionsButton = Primitive.button("options", () -> { transitionNext(new OptionsView()); - }); + }, false); var quitButton = Primitive.button("quit", () -> { var a = new QuitPopup(); a.show(Pos.CENTER); - }); + }, false); add(Pos.CENTER, Primitive.vbox( localButton, diff --git a/app/src/main/java/org/toop/app/widget/view/OnlineView.java b/app/src/main/java/org/toop/app/widget/view/OnlineView.java index 16ed1df..533e097 100644 --- a/app/src/main/java/org/toop/app/widget/view/OnlineView.java +++ b/app/src/main/java/org/toop/app/widget/view/OnlineView.java @@ -6,6 +6,14 @@ import org.toop.app.widget.complex.LabeledInputWidget; import org.toop.app.widget.complex.ViewWidget; import javafx.geometry.Pos; +import org.toop.framework.game.games.reversi.BitboardReversi; +import org.toop.framework.game.games.tictactoe.BitboardTicTacToe; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.networking.server.gateway.NettyGatewayServer; +import org.toop.framework.networking.server.stores.TurnBasedGameTypeStore; + +import java.time.Duration; +import java.util.concurrent.ConcurrentHashMap; public class OnlineView extends ViewWidget { public OnlineView() { @@ -21,7 +29,31 @@ public class OnlineView extends ViewWidget { serverPortInput.getValue(), playerNameInput.getValue() ); - }); + }, false); + + var localHostButton = Primitive.button("host!", () -> { + + var tps = new TurnBasedGameTypeStore(); + tps.register("tic-tac-toe", BitboardTicTacToe::new); + tps.register("reversi", BitboardReversi::new); + + var a = new NettyGatewayServer(6666, tps, Duration.ofSeconds(10)); + + new Thread(() -> { + try { + a.start(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }).start(); + + new Server( + "127.0.0.1", + "6666", + "host", + a + ); + }, false, false); add(Pos.CENTER, Primitive.vbox( serverInformationHeader, @@ -32,7 +64,9 @@ public class OnlineView extends ViewWidget { playerNameInput.getNode(), Primitive.separator(), - connectButton + connectButton, + Primitive.separator(), + localHostButton )); } } \ No newline at end of file diff --git a/app/src/main/java/org/toop/app/widget/view/ServerView.java b/app/src/main/java/org/toop/app/widget/view/ServerView.java index 7365e74..92c8101 100644 --- a/app/src/main/java/org/toop/app/widget/view/ServerView.java +++ b/app/src/main/java/org/toop/app/widget/view/ServerView.java @@ -1,26 +1,37 @@ package org.toop.app.widget.view; +import javafx.collections.FXCollections; +import javafx.css.converter.StringConverter; +import javafx.scene.control.ComboBox; import org.toop.app.widget.Primitive; import org.toop.app.widget.complex.ViewWidget; import java.util.List; +import java.util.Locale; import java.util.function.Consumer; import javafx.application.Platform; import javafx.geometry.Pos; import javafx.scene.control.Button; import javafx.scene.control.ListView; +import org.toop.framework.eventbus.EventFlow; +import org.toop.framework.networking.connection.events.NetworkEvents; public final class ServerView extends ViewWidget { private final String user; private final Consumer onPlayerClicked; + private final long clientId; + private final ComboBox gameList; private final ListView