added new classes for the games that use bitboards instead. also combined game with turnbasedgame

This commit is contained in:
ramollia
2025-12-03 12:51:09 +01:00
parent bc84171029
commit c25be04ff1
3 changed files with 288 additions and 0 deletions

View File

@@ -0,0 +1,71 @@
package org.toop.game;
import org.toop.framework.gameFramework.GameState;
import java.util.Arrays;
public abstract class BitboardGame {
private final int columnSize;
private final int rowSize;
// 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) {
this.columnSize = columnSize;
this.rowSize = rowSize;
this.playerBitboard = new long[playerCount];
this.currentTurn = 0;
Arrays.fill(playerBitboard, 0L);
}
public BitboardGame(BitboardGame other) {
this.columnSize = other.columnSize;
this.rowSize = other.rowSize;
this.playerBitboard = Arrays.copyOf(other.playerBitboard, other.playerBitboard.length);
this.currentTurn = other.currentTurn;
}
public int getColumnSize() {
return this.columnSize;
}
public int getRowSize() {
return this.rowSize;
}
public long getPlayerBitboard(int player) {
return this.playerBitboard[player];
}
public void setPlayerBitboard(int player, long bitboard) {
this.playerBitboard[player] = bitboard;
}
public int getPlayerCount() {
return playerBitboard.length;
}
public int getCurrentTurn() {
return currentTurn;
}
public int getCurrentPlayer() {
return currentTurn % playerBitboard.length;
}
public int getNextPlayer() {
return (currentTurn + 1) % playerBitboard.length;
}
public void nextTurn() {
currentTurn++;
}
public abstract long getLegalMoves();
public abstract GameState play(long move);
}

View File

@@ -0,0 +1,137 @@
package org.toop.game.reversi;
import org.toop.framework.gameFramework.GameState;
import org.toop.game.BitboardGame;
public class BitboardReversi extends BitboardGame {
public record Score(int black, int white) {}
private final long notAFile = 0xfefefefefefefefeL;
private final long notHFile = 0x7f7f7f7f7f7f7f7fL;
public BitboardReversi() {
super(8, 8, 2);
// Black (player 0)
setPlayerBitboard(0, (1L << (3 + 4 * 8)) | (1L << (4 + 3 * 8)));
// White (player 1)
setPlayerBitboard(1, (1L << (3 + 3 * 8)) | (1L << (4 + 4 * 8))); }
@Override
public long getLegalMoves() {
final long player = getPlayerBitboard(getCurrentPlayer());
final long opponent = getPlayerBitboard(getNextPlayer());
long legalMoves = 0L;
legalMoves |= computeMoves(player, opponent, 8, -1L);
legalMoves |= computeMoves(player, opponent, -8, -1L);
legalMoves |= computeMoves(player, opponent, 1, notAFile);
legalMoves |= computeMoves(player, opponent, -1, notHFile);
legalMoves |= computeMoves(player, opponent, 9, notAFile);
legalMoves |= computeMoves(player, opponent, 7, notHFile);
legalMoves |= computeMoves(player, opponent, -7, notHFile);
legalMoves |= computeMoves(player, opponent, -9, notAFile);
return legalMoves;
}
public long getFlips(long move) {
final long player = getPlayerBitboard(getCurrentPlayer());
final long opponent = getPlayerBitboard(getNextPlayer());
long flips = 0L;
flips |= computeFlips(move, player, opponent, 8, -1L);
flips |= computeFlips(move, player, opponent, -8, -1L);
flips |= computeFlips(move, player, opponent, 1, notAFile);
flips |= computeFlips(move, player, opponent, -1, notHFile);
flips |= computeFlips(move, player, opponent, 9, notAFile);
flips |= computeFlips(move, player, opponent, 7, notHFile);
flips |= computeFlips(move, player, opponent, -7, notHFile);
flips |= computeFlips(move, player, opponent, -9, notAFile);
return flips;
}
@Override
public GameState play(long move) {
final long flips = getFlips(move);
long player = getPlayerBitboard(getCurrentPlayer());
long opponent = getPlayerBitboard(getNextPlayer());
player |= move | flips;
opponent &= ~flips;
setPlayerBitboard(getCurrentPlayer(), player);
setPlayerBitboard(getNextPlayer(), opponent);
nextTurn();
final long nextLegalMoves = getLegalMoves();
if (nextLegalMoves <= 0) {
nextTurn();
final long skippedLegalMoves = getLegalMoves();
if (skippedLegalMoves <= 0) {
final long black = getPlayerBitboard(0);
final long white = getPlayerBitboard(1);
final int blackCount = Long.bitCount(black);
final int whiteCount = Long.bitCount(white);
if (blackCount == whiteCount) {
return GameState.DRAW;
}
return GameState.WIN;
}
return GameState.TURN_SKIPPED;
}
return GameState.NORMAL;
}
public Score getScore() {
return new Score(
Long.bitCount(getPlayerBitboard(0)),
Long.bitCount(getPlayerBitboard(1))
);
}
private long computeMoves(long player, long opponent, int shift, long mask) {
long moves = player;
while (true) {
if (shift > 0) moves = (moves << shift) & mask;
else moves = (moves >>> -shift) & mask;
long newMoves = moves & opponent;
if (newMoves == 0) break;
moves = newMoves;
}
if (shift > 0) moves = (moves << shift) & mask;
else moves = (moves >>> -shift) & mask;
return moves & ~(player | opponent);
}
private long computeFlips(long move, long player, long opponent, int shift, long mask) {
long flips = 0L;
long moves = move;
while (true) {
if (shift > 0) moves = (moves << shift) & mask;
else moves = (moves >>> -shift) & mask;
if ((moves & opponent) != 0) flips |= moves;
else if ((moves & player) != 0) return flips;
else return 0L;
}
}
}

View File

@@ -0,0 +1,80 @@
package org.toop.game.tictactoe;
import org.toop.framework.gameFramework.GameState;
import org.toop.game.BitboardGame;
public class BitboardTicTacToe extends BitboardGame {
private final long[] winningLines = {
0b111000000L, // top row
0b000111000L, // middle row
0b000000111L, // bottom row
0b100100100L, // left column
0b010010010L, // middle column
0b001001001L, // right column
0b100010001L, // diagonal
0b001010100L // anti-diagonal
};
public BitboardTicTacToe() {
super(3, 3, 2);
}
@Override
public long getLegalMoves() {
final long xBitboard = getPlayerBitboard(0);
final long oBitboard = getPlayerBitboard(1);
final long taken = (xBitboard | oBitboard);
return (~taken) & 0x1ffL;
}
@Override
public GameState play(long move) {
long playerBitboard = getPlayerBitboard(getCurrentPlayer());
playerBitboard |= move;
setPlayerBitboard(getCurrentPlayer(), playerBitboard);
if (checkWin(playerBitboard)) {
return GameState.WIN;
}
if (getLegalMoves() <= 0L || checkEarlyDraw()) {
return GameState.DRAW;
}
nextTurn();
return GameState.NORMAL;
}
private boolean checkWin(long board) {
for (final long line : winningLines) {
if ((board & line) == line) {
return true;
}
}
return false;
}
private boolean checkEarlyDraw() {
final long xBitboard = getPlayerBitboard(0);
final long oBitboard = getPlayerBitboard(1);
final long taken = (xBitboard | oBitboard);
final long empty = (~taken) & 0x1FFL;
for (final long line : winningLines) {
if (((line & xBitboard) != 0 && (line & oBitboard) != 0)) {
continue;
}
if ((line & empty) != 0) {
return false;
}
}
return true;
}
}