mirror of
https://github.com/2OOP/pism.git
synced 2026-02-04 10:54:51 +00:00
added new classes for the games that use bitboards instead. also combined game with turnbasedgame
This commit is contained in:
71
game/src/main/java/org/toop/game/BitboardGame.java
Normal file
71
game/src/main/java/org/toop/game/BitboardGame.java
Normal 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);
|
||||
}
|
||||
137
game/src/main/java/org/toop/game/reversi/BitboardReversi.java
Normal file
137
game/src/main/java/org/toop/game/reversi/BitboardReversi.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user