diff --git a/app/src/main/java/org/toop/app/Server.java b/app/src/main/java/org/toop/app/Server.java index c4227c0..9421004 100644 --- a/app/src/main/java/org/toop/app/Server.java +++ b/app/src/main/java/org/toop/app/Server.java @@ -20,6 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; public final class Server { private String user = ""; @@ -31,6 +32,8 @@ public final class Server { private ServerView view; private boolean isPolling = true; + private AtomicBoolean isSingleGame = new AtomicBoolean(false); + private ScheduledExecutorService scheduler; public static GameInformation.Type gameToType(String game) { @@ -93,9 +96,10 @@ public final class Server { private void sendChallenge(String opponent) { if (!isPolling) return; + ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> { - new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)) - .listen(NetworkEvents.GameMatchResponse.class, e -> { + new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)).postEvent(); + /* .listen(NetworkEvents.GameMatchResponse.class, e -> { if (e.clientId() == clientId) { isPolling = false; onlinePlayers.clear(); @@ -120,7 +124,9 @@ public final class Server { default -> ViewStack.push(new ErrorView("Unsupported game type.")); } } - }).postEvent(); + }) */ + ViewStack.pop(); + isSingleGame.set(true); })); } @@ -148,13 +154,16 @@ public final class Server { information.players[0].computerDifficulty = 5; information.players[1].name = response.opponent(); + Runnable onGameOverRunnable = isSingleGame.get()? null: this::gameOver; + + switch (type) { case TICTACTOE -> - new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); + new TicTacToeGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable); case REVERSI -> - new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); + new ReversiGame(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable); case CONNECT4 -> - new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage); + new Connect4Game(information, myTurn, this::forfeitGame, this::exitGame, this::sendMessage, onGameOverRunnable); default -> ViewStack.push(new ErrorView("Unsupported game type.")); } } @@ -166,11 +175,11 @@ public final class Server { String challengerName = extractQuotedValue(response.challengerName()); String gameType = extractQuotedValue(response.gameType()); final String finalGameType = gameType; - ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> { final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); ViewStack.pop(); + isSingleGame.set(true); //new EventFlow().listen(NetworkEvents.GameMatchResponse.class, e -> { @@ -200,8 +209,16 @@ public final class Server { startPopulateScheduler(); } + private void gameOver(){ + ViewStack.pop(); + ViewStack.push(view); + startPopulateScheduler(); + + } + private void startPopulateScheduler() { isPolling = true; + isSingleGame.set(false); stopScheduler(); new EventFlow() diff --git a/app/src/main/java/org/toop/app/game/Connect4Game.java b/app/src/main/java/org/toop/app/game/Connect4Game.java index 77bda9c..cd7c5d3 100644 --- a/app/src/main/java/org/toop/app/game/Connect4Game.java +++ b/app/src/main/java/org/toop/app/game/Connect4Game.java @@ -23,6 +23,7 @@ public class Connect4Game { private final GameInformation information; private final int myTurn; + private Runnable onGameOver; private final BlockingQueue moveQueue; private final Connect4 game; @@ -35,9 +36,10 @@ public class Connect4Game { private final AtomicBoolean isRunning; - public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage) { + public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage, Runnable onGameOver) { this.information = information; this.myTurn = myTurn; + this.onGameOver = onGameOver; moveQueue = new LinkedBlockingQueue(); @@ -97,7 +99,7 @@ public class Connect4Game { } public Connect4Game(GameInformation information) { - this(information, 0, null, null, null); + this(information, 0, null, null, null, null); } private void localGameThread() { while (isRunning.get()) { @@ -179,11 +181,14 @@ public class Connect4Game { if (state == Game.State.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { view.gameOver(true, information.players[0].name); + gameOver(); } else { view.gameOver(false, information.players[1].name); + gameOver(); } } else if (state == Game.State.DRAW) { view.gameOver(false, ""); + gameOver(); } } @@ -197,6 +202,14 @@ public class Connect4Game { setGameLabels(game.getCurrentTurn() == myTurn); } + private void gameOver() { + if (onGameOver == null){ + return; + } + isRunning.set(false); + onGameOver.run(); + } + private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(), "MESSAGE hoi")) .postEvent(); diff --git a/app/src/main/java/org/toop/app/game/ReversiGame.java b/app/src/main/java/org/toop/app/game/ReversiGame.java index f1b8045..e90c98d 100644 --- a/app/src/main/java/org/toop/app/game/ReversiGame.java +++ b/app/src/main/java/org/toop/app/game/ReversiGame.java @@ -27,7 +27,8 @@ public final class ReversiGame { private final GameInformation information; private final int myTurn; - private final BlockingQueue moveQueue; + private Runnable onGameOver; + private final BlockingQueue moveQueue; private final Reversi game; private final ReversiAI ai; @@ -38,11 +39,12 @@ public final class ReversiGame { private final AtomicBoolean isRunning; private final AtomicBoolean isPaused; - public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage) { + public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage, Runnable onGameOver) { this.information = information; this.myTurn = myTurn; - moveQueue = new LinkedBlockingQueue(); + this.onGameOver = onGameOver; + moveQueue = new LinkedBlockingQueue(); game = new Reversi(); ai = new ReversiAI(); @@ -102,7 +104,7 @@ public final class ReversiGame { } public ReversiGame(GameInformation information) { - this(information, 0, null, null, null); + this(information, 0, null, null, null,null); } private void localGameThread() { @@ -187,11 +189,14 @@ public final class ReversiGame { if (state == Game.State.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { view.gameOver(true, information.players[0].name); + gameOver(); } else { view.gameOver(false, information.players[1].name); + gameOver(); } } else if (state == Game.State.DRAW) { view.gameOver(false, ""); + game.play(move); } } @@ -199,6 +204,14 @@ public final class ReversiGame { setGameLabels(game.getCurrentTurn() == myTurn); } + private void gameOver() { + if (onGameOver == null){ + return; + } + isRunning.set(false); + onGameOver.run(); + } + private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { if (!isRunning.get()) { return; diff --git a/app/src/main/java/org/toop/app/game/TicTacToeGame.java b/app/src/main/java/org/toop/app/game/TicTacToeGame.java index 59ba72d..7e5db6b 100644 --- a/app/src/main/java/org/toop/app/game/TicTacToeGame.java +++ b/app/src/main/java/org/toop/app/game/TicTacToeGame.java @@ -24,7 +24,8 @@ public final class TicTacToeGame { private final GameInformation information; private final int myTurn; - private final BlockingQueue moveQueue; + private Runnable onGameOver; + private final BlockingQueue moveQueue; private final TicTacToe game; private final TicTacToeAI ai; @@ -34,11 +35,12 @@ public final class TicTacToeGame { private final AtomicBoolean isRunning; - public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage) { + public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer onMessage, Runnable onGameOver) { this.information = information; this.myTurn = myTurn; - moveQueue = new LinkedBlockingQueue(); + this.onGameOver = onGameOver; + moveQueue = new LinkedBlockingQueue(); game = new TicTacToe(); ai = new TicTacToeAI(); @@ -95,7 +97,7 @@ public final class TicTacToeGame { } public TicTacToeGame(GameInformation information) { - this(information, 0, null, null, null); + this(information, 0, null, null, null, null); } private void localGameThread() { @@ -177,12 +179,15 @@ public final class TicTacToeGame { if (state == Game.State.WIN) { if (response.player().equalsIgnoreCase(information.players[0].name)) { view.gameOver(true, information.players[0].name); + gameOver(); } else { view.gameOver(false, information.players[1].name); + gameOver(); } } else if (state == Game.State.DRAW) { if(game.getLegalMoves().length == 0) { //only return draw in online multiplayer if the game is actually over. view.gameOver(false, ""); + gameOver(); } } } @@ -196,6 +201,14 @@ public final class TicTacToeGame { setGameLabels(game.getCurrentTurn() == myTurn); } + private void gameOver() { + if (onGameOver == null){ + return; + } + isRunning.set(false); + onGameOver.run(); + } + private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { if (!isRunning.get()) { return; @@ -210,7 +223,14 @@ public final class TicTacToeGame { position = moveQueue.take().position(); } catch (InterruptedException _) {} } else { - final Game.Move move = ai.findBestMove(game, information.players[0].computerDifficulty); + final Game.Move move; + IO.println(information.players[0].name + " " + information.players[1].name); + if (information.players[1].name.equalsIgnoreCase("pism")) { + IO.println("got worst move"); + move = ai.findWorstMove(game,9); + }else{ + move = ai.findBestMove(game, information.players[0].computerDifficulty); + } assert move != null; position = move.position(); diff --git a/app/src/main/java/org/toop/app/view/views/ChallengeView.java b/app/src/main/java/org/toop/app/view/views/ChallengeView.java index 55b526f..a403de9 100644 --- a/app/src/main/java/org/toop/app/view/views/ChallengeView.java +++ b/app/src/main/java/org/toop/app/view/views/ChallengeView.java @@ -56,7 +56,7 @@ public final class ChallengeView extends View { final Button denyButton = button(); denyButton.setText(AppContext.getString("deny")); denyButton.setOnAction(_ -> { - ViewStack.pop(); + ViewStack.pop(); }); final List nodes = new ArrayList<>(); diff --git a/app/src/main/java/org/toop/app/view/views/GameView.java b/app/src/main/java/org/toop/app/view/views/GameView.java index 0ea669b..df69240 100644 --- a/app/src/main/java/org/toop/app/view/views/GameView.java +++ b/app/src/main/java/org/toop/app/view/views/GameView.java @@ -1,5 +1,6 @@ package org.toop.app.view.views; +import javafx.application.Platform; import org.toop.app.view.View; import org.toop.app.view.ViewStack; import org.toop.app.view.displays.SongDisplay; @@ -151,16 +152,19 @@ public final class GameView extends View { } public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) { - currentPlayerHeader.setText(currentPlayer); - currentMoveHeader.setText(currentMove); + Platform.runLater(() -> { + currentPlayerHeader.setText(currentPlayer); + currentMoveHeader.setText(currentMove); - nextPlayerHeader.setText(nextPlayer); + nextPlayerHeader.setText(nextPlayer); + + if (isMe) { + currentPlayerHeader.getStyleClass().add("my-turn"); + } else { + currentPlayerHeader.getStyleClass().remove("my-turn"); + } + }); - if (isMe) { - currentPlayerHeader.getStyleClass().add("my-turn"); - } else { - currentPlayerHeader.getStyleClass().remove("my-turn"); - } } public void updateChat(String message) { diff --git a/game/src/main/java/org/toop/game/reversi/ReversiAI.java b/game/src/main/java/org/toop/game/reversi/ReversiAI.java index 4f0865e..a532a62 100644 --- a/game/src/main/java/org/toop/game/reversi/ReversiAI.java +++ b/game/src/main/java/org/toop/game/reversi/ReversiAI.java @@ -6,6 +6,8 @@ import org.toop.game.Game; public final class ReversiAI extends AI { @Override public Game.Move findBestMove(Reversi game, int depth) { - return game.getLegalMoves()[0]; + Game.Move[] moves = game.getLegalMoves(); + int inty = (int)(Math.random() * moves.length-.5f); + return moves[inty]; } } diff --git a/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java b/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java index afc61b8..e780976 100644 --- a/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java +++ b/game/src/main/java/org/toop/game/tictactoe/TicTacToeAI.java @@ -11,7 +11,7 @@ public final class TicTacToeAI extends AI { final Game.Move[] legalMoves = game.getLegalMoves(); - if (legalMoves.length <= 0) { + if (legalMoves.length == 0) { return null; } @@ -38,6 +38,24 @@ public final class TicTacToeAI extends AI { return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; } + public Game.Move findWorstMove(TicTacToe game, int depth){ + + + Game.Move[] legalMoves = game.getLegalMoves(); + + int bestScore = -depth; + Game.Move bestMove = null; + + for (final Game.Move move : legalMoves) { + final int score = getMoveScore(game, depth, move, false); + + if (score > bestScore) { + bestMove = move; + bestScore = score; + } + } + return bestMove; + } private int getMoveScore(TicTacToe game, int depth, Game.Move move, boolean maximizing) { final TicTacToe copy = new TicTacToe(game);