From f4ee9921669ed09e980926a643aa20cfc863c404 Mon Sep 17 00:00:00 2001 From: Stef Date: Thu, 4 Dec 2025 00:49:49 +0100 Subject: [PATCH] Bitboard implemented with scuffed TicTacToe translation done by game. This should be done by the view. --- .../java/org/toop/app/canvas/GameCanvas.java | 3 +- .../org/toop/app/canvas/TicTacToeCanvas.java | 4 +- .../AbstractGameController.java | 10 ++--- .../gameControllers/ReversiController.java | 8 ++-- .../gameControllers/TicTacToeController.java | 16 ++++---- .../model/game/BoardProvider.java | 5 +++ .../model/game/TurnBasedGame.java | 2 +- .../main/java/org/toop/game/BitboardGame.java | 21 ++++++++-- .../game/games/reversi/BitboardReversi.java | 23 ++++++----- .../games/tictactoe/BitboardTicTacToe.java | 40 ++++++++++++++----- 10 files changed, 83 insertions(+), 49 deletions(-) create mode 100644 framework/src/main/java/org/toop/framework/gameFramework/model/game/BoardProvider.java 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 0e78ab4..5453b82 100644 --- a/app/src/main/java/org/toop/app/canvas/GameCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/GameCanvas.java @@ -9,10 +9,11 @@ import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.util.Duration; import org.toop.framework.gameFramework.model.game.AbstractGame; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import java.util.function.Consumer; -public abstract class GameCanvas implements DrawPlayerMove, DrawPlayerHover { +public abstract class GameCanvas> implements DrawPlayerMove, DrawPlayerHover { 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 && diff --git a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java index 41d398f..090fefc 100644 --- a/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java +++ b/app/src/main/java/org/toop/app/canvas/TicTacToeCanvas.java @@ -2,11 +2,11 @@ package org.toop.app.canvas; import javafx.scene.paint.Color; import org.toop.framework.gameFramework.model.game.AbstractGame; -import org.toop.game.games.tictactoe.TicTacToeR; +import org.toop.game.games.tictactoe.BitboardTicTacToe; import java.util.function.Consumer; -public final class TicTacToeCanvas extends GameCanvas { +public final class TicTacToeCanvas extends GameCanvas { public TicTacToeCanvas(Color color, int width, int height, Consumer onCellClicked) { super(color, Color.TRANSPARENT, width, height, 3, 3, 30, false, onCellClicked,null); } diff --git a/app/src/main/java/org/toop/app/gameControllers/AbstractGameController.java b/app/src/main/java/org/toop/app/gameControllers/AbstractGameController.java index 7d670e4..cd7e3cd 100644 --- a/app/src/main/java/org/toop/app/gameControllers/AbstractGameController.java +++ b/app/src/main/java/org/toop/app/gameControllers/AbstractGameController.java @@ -3,6 +3,7 @@ package org.toop.app.gameControllers; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.toop.framework.gameFramework.controller.UpdatesGameUI; +import org.toop.framework.gameFramework.model.game.TurnBasedGame; import org.toop.framework.gameFramework.view.GUIEvents; import org.toop.app.canvas.GameCanvas; import org.toop.framework.networking.events.NetworkEvents; @@ -18,7 +19,7 @@ import java.util.ArrayList; import java.util.List; import java.util.function.Consumer; -public abstract class AbstractGameController> implements UpdatesGameUI, ThreadBehaviour { +public abstract class AbstractGameController> implements UpdatesGameUI, ThreadBehaviour { protected final EventFlow eventFlow = new EventFlow(); protected final List> listeners = new ArrayList<>(); @@ -33,18 +34,13 @@ public abstract class AbstractGameController> implemen protected final GameCanvas canvas; private final Player[] players; // List of players, can't be changed. - protected final T 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. // TODO: Make visualisation of moves a behaviour. protected AbstractGameController(GameCanvas canvas, Player[] players, T game, ThreadBehaviour gameThreadBehaviour, String gameType) { logger.info("Creating AbstractGameController"); - // Make sure player list matches expected size - if (players.length != game.getPlayerCount()){ - logger.error("Player count mismatch"); - throw new IllegalArgumentException("players and game's players must have same length"); - } this.canvas = canvas; this.players = players; diff --git a/app/src/main/java/org/toop/app/gameControllers/ReversiController.java b/app/src/main/java/org/toop/app/gameControllers/ReversiController.java index 3c46306..95ce463 100644 --- a/app/src/main/java/org/toop/app/gameControllers/ReversiController.java +++ b/app/src/main/java/org/toop/app/gameControllers/ReversiController.java @@ -33,7 +33,7 @@ public class ReversiController extends AbstractGameController { private void onHoverMove(GUIEvents.PlayerMoveHovered event){ int cellEntered = event.move(); - canvas.drawPlayerHover(-1, cellEntered, game); + //canvas.drawPlayerHover(-1, cellEntered, game); /*// (information.players[game.getCurrentTurn()].isHuman) { int[] legalMoves = game.getLegalMoves(); boolean isLegalMove = false; @@ -72,20 +72,20 @@ public class ReversiController extends AbstractGameController { } } - final int[] flipped = game.getMostRecentlyFlippedPieces(); + //final int[] flipped = game.getMostRecentlyFlippedPieces(); final SequentialTransition animation = new SequentialTransition(); final Color fromColor = getCurrentPlayerIndex() == 0? Color.WHITE : Color.BLACK; final Color toColor = getCurrentPlayerIndex() == 0? Color.BLACK : Color.WHITE; - if (animate && flipped != null) { + /*if (animate && flipped != null) { for (final int flip : flipped) { canvas.clear(flip); canvas.drawDot(fromColor, flip); animation.getChildren().addFirst(canvas.flipDot(fromColor, toColor, flip)); } - } + }*/ animation.setOnFinished(_ -> { diff --git a/app/src/main/java/org/toop/app/gameControllers/TicTacToeController.java b/app/src/main/java/org/toop/app/gameControllers/TicTacToeController.java index 6bd6fb0..2003604 100644 --- a/app/src/main/java/org/toop/app/gameControllers/TicTacToeController.java +++ b/app/src/main/java/org/toop/app/gameControllers/TicTacToeController.java @@ -12,17 +12,17 @@ import org.toop.game.gameThreads.LocalThreadBehaviour; import org.toop.game.gameThreads.OnlineThreadBehaviour; import org.toop.game.players.LocalPlayer; import org.toop.app.widget.WidgetContainer; -import org.toop.game.games.tictactoe.TicTacToeR; +import org.toop.game.games.tictactoe.BitboardTicTacToe; -public class TicTacToeController extends AbstractGameController { +public class TicTacToeController extends AbstractGameController { - public TicTacToeController(Player[] players, boolean local) { - TicTacToeR ticTacToeR = new TicTacToeR(players); + public TicTacToeController(Player[] players, boolean local) { + BitboardTicTacToe BitboardTicTacToe = new BitboardTicTacToe(players); super( new TicTacToeCanvas(Color.GRAY, (App.getHeight() / 4) * 3, (App.getHeight() / 4) * 3,(c) -> {new EventFlow().addPostEvent(GUIEvents.PlayerAttemptedMove.class, c).postEvent();}), players, - ticTacToeR, - local ? new LocalThreadBehaviour(ticTacToeR, players) : new OnlineThreadBehaviour<>(ticTacToeR, players), // TODO: Player order matters here, this won't work atm + BitboardTicTacToe, + local ? new LocalThreadBehaviour(BitboardTicTacToe, players) : new OnlineThreadBehaviour<>(BitboardTicTacToe, players), // TODO: Player order matters here, this won't work atm "TicTacToe"); initUI(); @@ -31,7 +31,7 @@ public class TicTacToeController extends AbstractGameController { //new EventFlow().listen(GUIEvents.PlayerAttemptedMove.class, event -> {if (getCurrentPlayer() instanceof LocalPlayer lp){lp.setMove(event.move());}}); } - public TicTacToeController(Player[] players) { + public TicTacToeController(Player[] players) { this(players, true); } @@ -53,7 +53,7 @@ public class TicTacToeController extends AbstractGameController { int[] board = game.getBoard(); // Draw each square - for (int i = 0; i < board.length; i++){ + for (int i = 0; i < 9; i++){ // If square isn't empty, draw player move if (board[i] != AbstractGame.EMPTY){ canvas.drawPlayerMove(board[i], i); diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/BoardProvider.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/BoardProvider.java new file mode 100644 index 0000000..06ab870 --- /dev/null +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/BoardProvider.java @@ -0,0 +1,5 @@ +package org.toop.framework.gameFramework.model.game; + +public interface BoardProvider { + int[] getBoard(); +} diff --git a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java index 508637a..8469b45 100644 --- a/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java +++ b/framework/src/main/java/org/toop/framework/gameFramework/model/game/TurnBasedGame.java @@ -1,5 +1,5 @@ package org.toop.framework.gameFramework.model.game; -public interface TurnBasedGame> extends Playable, DeepCopyable, PlayerProvider { +public interface TurnBasedGame> extends Playable, DeepCopyable, PlayerProvider, BoardProvider { int getCurrentTurn(); } diff --git a/game/src/main/java/org/toop/game/BitboardGame.java b/game/src/main/java/org/toop/game/BitboardGame.java index 1cf7295..e312279 100644 --- a/game/src/main/java/org/toop/game/BitboardGame.java +++ b/game/src/main/java/org/toop/game/BitboardGame.java @@ -2,23 +2,28 @@ package org.toop.game; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.TurnBasedGame; +import org.toop.framework.gameFramework.model.player.Player; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -public abstract class BitboardGame implements TurnBasedGame { +public abstract class BitboardGame> implements TurnBasedGame { private final int columnSize; private final int rowSize; + private Player[] players; + // long is 64 bits. Every game has a limit of 64 cells maximum. private final long[] playerBitboard; private int currentTurn; - public BitboardGame(int columnSize, int rowSize, int playerCount) { + public BitboardGame(int columnSize, int rowSize, int playerCount, Player[] players) { this.columnSize = columnSize; this.rowSize = rowSize; + this.players = players; + this.playerBitboard = new long[playerCount]; this.currentTurn = 0; @@ -34,6 +39,7 @@ public abstract class BitboardGame implements TurnBasedGame { j++; } } + System.out.println(Arrays.toString(output)); return output; } @@ -43,6 +49,7 @@ public abstract class BitboardGame implements TurnBasedGame { protected int[] translateBoard(){ int[] output = new int[64]; + Arrays.fill(output, -1); for(int i = 0; i < this.playerBitboard.length; i++){ for (int j = 0; j < 64; j++){ if ((this.playerBitboard[i] & (1L << j)) != 0){ @@ -82,10 +89,12 @@ public abstract class BitboardGame implements TurnBasedGame { } public int getCurrentTurn() { - return currentTurn; + return getCurrentPlayerIndex(); } - public int getCurrentPlayer() { + public Player getPlayer(int index) {return players[index];} + + public int getCurrentPlayerIndex() { return currentTurn % playerBitboard.length; } @@ -93,6 +102,10 @@ public abstract class BitboardGame implements TurnBasedGame { return (currentTurn + 1) % playerBitboard.length; } + public Player getCurrentPlayer(){ + return players[getCurrentPlayerIndex()]; + } + public void nextTurn() { currentTurn++; } diff --git a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java b/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java index 028ccfb..a898557 100644 --- a/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java +++ b/game/src/main/java/org/toop/game/games/reversi/BitboardReversi.java @@ -1,14 +1,15 @@ -package org.toop.game.reversi; +package org.toop.game.games.reversi; import org.toop.framework.gameFramework.GameState; import org.toop.framework.gameFramework.model.game.PlayResult; import org.toop.framework.gameFramework.model.player.Player; import org.toop.game.BitboardGame; -public class BitboardReversi extends BitboardGame { +public class BitboardReversi extends BitboardGame { + @Override - public Player getPlayer(int index) { - return null; + public int[] getBoard() { + return translateBoard(); } public record Score(int black, int white) {} @@ -16,8 +17,8 @@ public class BitboardReversi extends BitboardGame { private final long notAFile = 0xfefefefefefefefeL; private final long notHFile = 0x7f7f7f7f7f7f7f7fL; - public BitboardReversi() { - super(8, 8, 2); + public BitboardReversi(Player[] players) { + super(8, 8, 2, players); // Black (player 0) setPlayerBitboard(0, (1L << (3 + 4 * 8)) | (1L << (4 + 3 * 8))); @@ -26,7 +27,7 @@ public class BitboardReversi extends BitboardGame { setPlayerBitboard(1, (1L << (3 + 3 * 8)) | (1L << (4 + 4 * 8))); } public long getLegalMoves2() { - final long player = getPlayerBitboard(getCurrentPlayer()); + final long player = getPlayerBitboard(getCurrentPlayerIndex()); final long opponent = getPlayerBitboard(getNextPlayer()); long legalMoves = 0L; @@ -43,7 +44,7 @@ public class BitboardReversi extends BitboardGame { } public long getFlips(long move) { - final long player = getPlayerBitboard(getCurrentPlayer()); + final long player = getPlayerBitboard(getCurrentPlayerIndex()); final long opponent = getPlayerBitboard(getNextPlayer()); long flips = 0L; @@ -66,7 +67,7 @@ public class BitboardReversi extends BitboardGame { @Override public PlayResult play(int move) { - return new PlayResult(playBit(translateMove(move)), getCurrentPlayer()); + return new PlayResult(playBit(translateMove(move)), getCurrentPlayerIndex()); } // TODO: Implement @@ -76,13 +77,13 @@ public class BitboardReversi extends BitboardGame { public GameState playBit(long move) { final long flips = getFlips(move); - long player = getPlayerBitboard(getCurrentPlayer()); + long player = getPlayerBitboard(getCurrentPlayerIndex()); long opponent = getPlayerBitboard(getNextPlayer()); player |= move | flips; opponent &= ~flips; - setPlayerBitboard(getCurrentPlayer(), player); + setPlayerBitboard(getCurrentPlayerIndex(), player); setPlayerBitboard(getNextPlayer(), opponent); nextTurn(); diff --git a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java b/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java index b955fb8..fff6e88 100644 --- a/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java +++ b/game/src/main/java/org/toop/game/games/tictactoe/BitboardTicTacToe.java @@ -1,9 +1,11 @@ -package org.toop.game.tictactoe; +package org.toop.game.games.tictactoe; import org.toop.framework.gameFramework.GameState; +import org.toop.framework.gameFramework.model.game.PlayResult; +import org.toop.framework.gameFramework.model.player.Player; import org.toop.game.BitboardGame; -public class BitboardTicTacToe extends BitboardGame { +public class BitboardTicTacToe extends BitboardGame { private final long[] winningLines = { 0b111000000L, // top row 0b000111000L, // middle row @@ -15,8 +17,8 @@ public class BitboardTicTacToe extends BitboardGame { 0b001010100L // anti-diagonal }; - public BitboardTicTacToe() { - super(3, 3, 2); + public BitboardTicTacToe(Player[] players) { + super(3, 3, 2, players); } @Override @@ -24,7 +26,12 @@ public class BitboardTicTacToe extends BitboardGame { return translateLegalMoves(getLegalMoves2()); } - public long getLegalMoves2() { + @Override + public PlayResult play(int move) { + return new PlayResult(play2(translateMove(move)), getCurrentPlayerIndex()); + } + + public long getLegalMoves2() { final long xBitboard = getPlayerBitboard(0); final long oBitboard = getPlayerBitboard(1); @@ -32,22 +39,22 @@ public class BitboardTicTacToe extends BitboardGame { return (~taken) & 0x1ffL; } - @Override - public GameState play(long move) { - long playerBitboard = getPlayerBitboard(getCurrentPlayer()); + public GameState play2(long move) { + long playerBitboard = getPlayerBitboard(getCurrentPlayerIndex()); playerBitboard |= move; - setPlayerBitboard(getCurrentPlayer(), playerBitboard); + setPlayerBitboard(getCurrentPlayerIndex(), playerBitboard); + nextTurn(); if (checkWin(playerBitboard)) { return GameState.WIN; } - if (getLegalMoves() <= 0L || checkEarlyDraw()) { + if (getLegalMoves2() <= 0L || checkEarlyDraw()) { return GameState.DRAW; } - nextTurn(); + return GameState.NORMAL; } @@ -81,4 +88,15 @@ public class BitboardTicTacToe extends BitboardGame { return true; } + + @Override + public int[] getBoard() { + return translateBoard(); + } + + // TODO: Implement + @Override + public BitboardTicTacToe deepCopy() { + return this; + } } \ No newline at end of file