tourney ready

This commit is contained in:
Ticho Hidding
2025-10-27 17:14:36 +01:00
parent b506afdade
commit c115fb91af
8 changed files with 116 additions and 29 deletions

View File

@@ -20,6 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
public final class Server { public final class Server {
private String user = ""; private String user = "";
@@ -31,6 +32,8 @@ public final class Server {
private ServerView view; private ServerView view;
private boolean isPolling = true; private boolean isPolling = true;
private AtomicBoolean isSingleGame = new AtomicBoolean(false);
private ScheduledExecutorService scheduler; private ScheduledExecutorService scheduler;
public static GameInformation.Type gameToType(String game) { public static GameInformation.Type gameToType(String game) {
@@ -93,9 +96,10 @@ public final class Server {
private void sendChallenge(String opponent) { private void sendChallenge(String opponent) {
if (!isPolling) return; if (!isPolling) return;
ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> { ViewStack.push(new SendChallengeView(this, opponent, (playerInformation, gameType) -> {
new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)) new EventFlow().addPostEvent(new NetworkEvents.SendChallenge(clientId, opponent, gameType)).postEvent();
.listen(NetworkEvents.GameMatchResponse.class, e -> { /* .listen(NetworkEvents.GameMatchResponse.class, e -> {
if (e.clientId() == clientId) { if (e.clientId() == clientId) {
isPolling = false; isPolling = false;
onlinePlayers.clear(); onlinePlayers.clear();
@@ -120,7 +124,9 @@ public final class Server {
default -> ViewStack.push(new ErrorView("Unsupported game type.")); 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[0].computerDifficulty = 5;
information.players[1].name = response.opponent(); information.players[1].name = response.opponent();
Runnable onGameOverRunnable = isSingleGame.get()? null: this::gameOver;
switch (type) { switch (type) {
case TICTACTOE -> 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 -> 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 -> 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.")); default -> ViewStack.push(new ErrorView("Unsupported game type."));
} }
} }
@@ -166,11 +175,11 @@ public final class Server {
String challengerName = extractQuotedValue(response.challengerName()); String challengerName = extractQuotedValue(response.challengerName());
String gameType = extractQuotedValue(response.gameType()); String gameType = extractQuotedValue(response.gameType());
final String finalGameType = gameType; final String finalGameType = gameType;
ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> { ViewStack.push(new ChallengeView(challengerName, gameType, (playerInformation) -> {
final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", "")); final int challengeId = Integer.parseInt(response.challengeId().replaceAll("\\D", ""));
new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent(); new EventFlow().addPostEvent(new NetworkEvents.SendAcceptChallenge(clientId, challengeId)).postEvent();
ViewStack.pop(); ViewStack.pop();
isSingleGame.set(true);
//new EventFlow().listen(NetworkEvents.GameMatchResponse.class, e -> { //new EventFlow().listen(NetworkEvents.GameMatchResponse.class, e -> {
@@ -200,8 +209,16 @@ public final class Server {
startPopulateScheduler(); startPopulateScheduler();
} }
private void gameOver(){
ViewStack.pop();
ViewStack.push(view);
startPopulateScheduler();
}
private void startPopulateScheduler() { private void startPopulateScheduler() {
isPolling = true; isPolling = true;
isSingleGame.set(false);
stopScheduler(); stopScheduler();
new EventFlow() new EventFlow()

View File

@@ -23,6 +23,7 @@ public class Connect4Game {
private final GameInformation information; private final GameInformation information;
private final int myTurn; private final int myTurn;
private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Game.Move> moveQueue;
private final Connect4 game; private final Connect4 game;
@@ -35,9 +36,10 @@ public class Connect4Game {
private final AtomicBoolean isRunning; private final AtomicBoolean isRunning;
public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) { public Connect4Game(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information; this.information = information;
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Game.Move>();
@@ -97,7 +99,7 @@ public class Connect4Game {
} }
public Connect4Game(GameInformation information) { public Connect4Game(GameInformation information) {
this(information, 0, null, null, null); this(information, 0, null, null, null, null);
} }
private void localGameThread() { private void localGameThread() {
while (isRunning.get()) { while (isRunning.get()) {
@@ -179,11 +181,14 @@ public class Connect4Game {
if (state == Game.State.WIN) { if (state == Game.State.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) { if (response.player().equalsIgnoreCase(information.players[0].name)) {
view.gameOver(true, information.players[0].name); view.gameOver(true, information.players[0].name);
gameOver();
} else { } else {
view.gameOver(false, information.players[1].name); view.gameOver(false, information.players[1].name);
gameOver();
} }
} else if (state == Game.State.DRAW) { } else if (state == Game.State.DRAW) {
view.gameOver(false, ""); view.gameOver(false, "");
gameOver();
} }
} }
@@ -197,6 +202,14 @@ public class Connect4Game {
setGameLabels(game.getCurrentTurn() == myTurn); setGameLabels(game.getCurrentTurn() == myTurn);
} }
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(), "MESSAGE hoi")) new EventFlow().addPostEvent(new NetworkEvents.SendCommand(response.clientId(), "MESSAGE hoi"))
.postEvent(); .postEvent();

View File

@@ -27,6 +27,7 @@ public final class ReversiGame {
private final GameInformation information; private final GameInformation information;
private final int myTurn; private final int myTurn;
private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Game.Move> moveQueue;
private final Reversi game; private final Reversi game;
@@ -38,10 +39,11 @@ public final class ReversiGame {
private final AtomicBoolean isRunning; private final AtomicBoolean isRunning;
private final AtomicBoolean isPaused; private final AtomicBoolean isPaused;
public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) { public ReversiGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information; this.information = information;
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Game.Move>();
game = new Reversi(); game = new Reversi();
@@ -102,7 +104,7 @@ public final class ReversiGame {
} }
public ReversiGame(GameInformation information) { public ReversiGame(GameInformation information) {
this(information, 0, null, null, null); this(information, 0, null, null, null,null);
} }
private void localGameThread() { private void localGameThread() {
@@ -187,11 +189,14 @@ public final class ReversiGame {
if (state == Game.State.WIN) { if (state == Game.State.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) { if (response.player().equalsIgnoreCase(information.players[0].name)) {
view.gameOver(true, information.players[0].name); view.gameOver(true, information.players[0].name);
gameOver();
} else { } else {
view.gameOver(false, information.players[1].name); view.gameOver(false, information.players[1].name);
gameOver();
} }
} else if (state == Game.State.DRAW) { } else if (state == Game.State.DRAW) {
view.gameOver(false, ""); view.gameOver(false, "");
game.play(move);
} }
} }
@@ -199,6 +204,14 @@ public final class ReversiGame {
setGameLabels(game.getCurrentTurn() == myTurn); setGameLabels(game.getCurrentTurn() == myTurn);
} }
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) { if (!isRunning.get()) {
return; return;

View File

@@ -24,6 +24,7 @@ public final class TicTacToeGame {
private final GameInformation information; private final GameInformation information;
private final int myTurn; private final int myTurn;
private Runnable onGameOver;
private final BlockingQueue<Game.Move> moveQueue; private final BlockingQueue<Game.Move> moveQueue;
private final TicTacToe game; private final TicTacToe game;
@@ -34,10 +35,11 @@ public final class TicTacToeGame {
private final AtomicBoolean isRunning; private final AtomicBoolean isRunning;
public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage) { public TicTacToeGame(GameInformation information, int myTurn, Runnable onForfeit, Runnable onExit, Consumer<String> onMessage, Runnable onGameOver) {
this.information = information; this.information = information;
this.myTurn = myTurn; this.myTurn = myTurn;
this.onGameOver = onGameOver;
moveQueue = new LinkedBlockingQueue<Game.Move>(); moveQueue = new LinkedBlockingQueue<Game.Move>();
game = new TicTacToe(); game = new TicTacToe();
@@ -95,7 +97,7 @@ public final class TicTacToeGame {
} }
public TicTacToeGame(GameInformation information) { public TicTacToeGame(GameInformation information) {
this(information, 0, null, null, null); this(information, 0, null, null, null, null);
} }
private void localGameThread() { private void localGameThread() {
@@ -177,12 +179,15 @@ public final class TicTacToeGame {
if (state == Game.State.WIN) { if (state == Game.State.WIN) {
if (response.player().equalsIgnoreCase(information.players[0].name)) { if (response.player().equalsIgnoreCase(information.players[0].name)) {
view.gameOver(true, information.players[0].name); view.gameOver(true, information.players[0].name);
gameOver();
} else { } else {
view.gameOver(false, information.players[1].name); view.gameOver(false, information.players[1].name);
gameOver();
} }
} else if (state == Game.State.DRAW) { } else if (state == Game.State.DRAW) {
if(game.getLegalMoves().length == 0) { //only return draw in online multiplayer if the game is actually over. if(game.getLegalMoves().length == 0) { //only return draw in online multiplayer if the game is actually over.
view.gameOver(false, ""); view.gameOver(false, "");
gameOver();
} }
} }
} }
@@ -196,6 +201,14 @@ public final class TicTacToeGame {
setGameLabels(game.getCurrentTurn() == myTurn); setGameLabels(game.getCurrentTurn() == myTurn);
} }
private void gameOver() {
if (onGameOver == null){
return;
}
isRunning.set(false);
onGameOver.run();
}
private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) { private void onYourTurnResponse(NetworkEvents.YourTurnResponse response) {
if (!isRunning.get()) { if (!isRunning.get()) {
return; return;
@@ -210,7 +223,14 @@ public final class TicTacToeGame {
position = moveQueue.take().position(); position = moveQueue.take().position();
} catch (InterruptedException _) {} } catch (InterruptedException _) {}
} else { } 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; assert move != null;
position = move.position(); position = move.position();

View File

@@ -1,5 +1,6 @@
package org.toop.app.view.views; package org.toop.app.view.views;
import javafx.application.Platform;
import org.toop.app.view.View; import org.toop.app.view.View;
import org.toop.app.view.ViewStack; import org.toop.app.view.ViewStack;
import org.toop.app.view.displays.SongDisplay; import org.toop.app.view.displays.SongDisplay;
@@ -151,6 +152,7 @@ public final class GameView extends View {
} }
public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) { public void nextPlayer(boolean isMe, String currentPlayer, String currentMove, String nextPlayer) {
Platform.runLater(() -> {
currentPlayerHeader.setText(currentPlayer); currentPlayerHeader.setText(currentPlayer);
currentMoveHeader.setText(currentMove); currentMoveHeader.setText(currentMove);
@@ -161,6 +163,8 @@ public final class GameView extends View {
} else { } else {
currentPlayerHeader.getStyleClass().remove("my-turn"); currentPlayerHeader.getStyleClass().remove("my-turn");
} }
});
} }
public void updateChat(String message) { public void updateChat(String message) {

View File

@@ -6,6 +6,8 @@ import org.toop.game.Game;
public final class ReversiAI extends AI<Reversi> { public final class ReversiAI extends AI<Reversi> {
@Override @Override
public Game.Move findBestMove(Reversi game, int depth) { 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];
} }
} }

View File

@@ -11,7 +11,7 @@ public final class TicTacToeAI extends AI<TicTacToe> {
final Game.Move[] legalMoves = game.getLegalMoves(); final Game.Move[] legalMoves = game.getLegalMoves();
if (legalMoves.length <= 0) { if (legalMoves.length == 0) {
return null; return null;
} }
@@ -38,6 +38,24 @@ public final class TicTacToeAI extends AI<TicTacToe> {
return bestMove != null? bestMove : legalMoves[(int)(Math.random() * legalMoves.length)]; 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) { private int getMoveScore(TicTacToe game, int depth, Game.Move move, boolean maximizing) {
final TicTacToe copy = new TicTacToe(game); final TicTacToe copy = new TicTacToe(game);