457 lines
14 KiB
Java
457 lines
14 KiB
Java
package backend;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.HashSet;
|
|
import java.util.Set;
|
|
import java.util.Stack;
|
|
|
|
public class Board {
|
|
|
|
private int colNum;
|
|
private int lineNum;
|
|
private Piece[][] board;
|
|
private Stack<MoveRecord> moveHistory = new Stack<>();
|
|
private EnPassant enPassant = new EnPassant();
|
|
private int turnNumber = 0;
|
|
private boolean isWhiteTurn = true;
|
|
|
|
private Piece selectedPiece = null;
|
|
private Set<String> highlightedPositions = new HashSet<>();
|
|
|
|
public Board(int colNum, int lineNum) {
|
|
this.colNum = colNum;
|
|
this.lineNum = lineNum;
|
|
this.board = new Piece[lineNum][colNum];
|
|
}
|
|
|
|
public Set<String> getAllMoves(boolean isWhite) {
|
|
Set<String> allMoves = new HashSet<>();
|
|
for (Piece piece : getPieces()) {
|
|
if (piece.isWhite() == isWhite) {
|
|
allMoves.addAll(getValidMoves(piece, true));
|
|
}
|
|
}
|
|
return allMoves;
|
|
}
|
|
|
|
public int getWidth() {
|
|
return colNum;
|
|
}
|
|
|
|
public int getHeight() {
|
|
return lineNum;
|
|
}
|
|
|
|
public int getTurnNumber() { //for checking
|
|
return turnNumber;
|
|
}
|
|
|
|
public Piece[][] getBoardMatrix() {
|
|
return board;
|
|
}
|
|
|
|
|
|
public boolean isTurnWhite() {
|
|
return isWhiteTurn;
|
|
}
|
|
|
|
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
|
board[y][x] = new Piece(x, y, type, isWhite);
|
|
}
|
|
|
|
public void populateBoard() {
|
|
cleanBoard();
|
|
for (int x = 0; x < getWidth(); x++) {
|
|
setPiece(false, PieceType.Pawn, x, 1);
|
|
setPiece(true, PieceType.Pawn, x, 6);
|
|
}
|
|
setPiece(false, PieceType.Rook, 0, 0);
|
|
setPiece(false, PieceType.Rook, 7, 0);
|
|
setPiece(true, PieceType.Rook, 0, 7);
|
|
setPiece(true, PieceType.Rook, 7, 7);
|
|
setPiece(false, PieceType.Knight, 1, 0);
|
|
setPiece(false, PieceType.Knight, 6, 0);
|
|
setPiece(true, PieceType.Knight, 1, 7);
|
|
setPiece(true, PieceType.Knight, 6, 7);
|
|
setPiece(false, PieceType.Bishop, 2, 0);
|
|
setPiece(false, PieceType.Bishop, 5, 0);
|
|
setPiece(true, PieceType.Bishop, 2, 7);
|
|
setPiece(true, PieceType.Bishop, 5, 7);
|
|
setPiece(false, PieceType.Queen, 3, 0);
|
|
setPiece(true, PieceType.Queen, 3, 7);
|
|
setPiece(false, PieceType.King, 4, 0);
|
|
setPiece(true, PieceType.King, 4, 7);
|
|
}
|
|
|
|
public void cleanBoard() {
|
|
for (int y = 0; y < getHeight(); y++) { //each column
|
|
for (int x = 0; x < getWidth(); x++) { //each row
|
|
board[y][x] = null; //starts at 0,0
|
|
}
|
|
}
|
|
selectedPiece = null;
|
|
highlightedPositions.clear();
|
|
}
|
|
|
|
public ArrayList<Piece> getPieces() {
|
|
ArrayList<Piece> pieces = new ArrayList<>();
|
|
for (int y = 0; y < getHeight(); y++) {
|
|
for (int x = 0; x < getWidth(); x++) {
|
|
if (board[y][x] != null) {
|
|
pieces.add(board[y][x]);
|
|
}
|
|
}
|
|
}
|
|
return pieces;
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int y = 0; y < getHeight(); y++) {
|
|
for (int x = 0; x < getWidth(); x++) {
|
|
Piece piece = board[y][x];
|
|
if (piece == null) {
|
|
sb.append(". ");
|
|
} else {
|
|
String symbol = piece.getType().getSummary();
|
|
sb.append(piece.isWhite() ? symbol.toUpperCase() : symbol.toLowerCase()).append(" ");
|
|
}
|
|
}
|
|
sb.append("\n");
|
|
}
|
|
return sb.toString();
|
|
}
|
|
|
|
public void userTouch(int x, int y) {
|
|
if (selectedPiece == null) {
|
|
Piece p = board[y][x];
|
|
if (p != null && p.isWhite() == isWhiteTurn) {
|
|
selectedPiece = p;
|
|
highlightedPositions = getValidMoves(p);
|
|
}
|
|
}
|
|
else {
|
|
String targetPos = x + "," + y;
|
|
if (highlightedPositions.contains(targetPos)) {
|
|
if (selectedPiece.getType() == PieceType.King && Math.abs(x - selectedPiece.getX()) == 2) {
|
|
// Castling move
|
|
Castling castlingHandler = new Castling(this);
|
|
int rookX = (x > selectedPiece.getX()) ? 7 : 0;
|
|
castlingHandler.performCastle(selectedPiece, rookX, y);
|
|
} else {
|
|
// Normal move
|
|
movePiece(selectedPiece.getX(), selectedPiece.getY(), x, y);
|
|
}
|
|
isWhiteTurn = !isWhiteTurn;
|
|
turnNumber++;
|
|
}
|
|
selectedPiece = null;
|
|
highlightedPositions.clear();
|
|
}
|
|
}
|
|
|
|
public boolean isSelected(int x, int y) {
|
|
return selectedPiece != null && selectedPiece.getX() == x && selectedPiece.getY() == y;
|
|
}
|
|
|
|
public boolean isHighlighted(int x, int y) {
|
|
return highlightedPositions.contains(x + "," + y);
|
|
}
|
|
|
|
void movePiece(int fromX, int fromY, int toX, int toY) { //from private to not
|
|
Piece moving = board[fromY][fromX];
|
|
Piece captured = board[toY][toX];
|
|
|
|
// For en passant
|
|
if (enPassant.isEnPassantCapture(moving, fromX, toX, toY, board)) {
|
|
int capturedY = moving.isWhite() ? toY + 1 : toY - 1;
|
|
captured = board[capturedY][toX];
|
|
board[capturedY][toX] = null;
|
|
}
|
|
|
|
// Save the move
|
|
moveHistory.push(new MoveRecord(
|
|
new Piece(moving.getX(), moving.getY(), moving.getType(), moving.isWhite()),
|
|
captured == null ? null : new Piece(captured.getX(), captured.getY(), captured.getType(), captured.isWhite()),
|
|
fromX, fromY, toX, toY,
|
|
turnNumber, isWhiteTurn,
|
|
enPassant.getTarget()
|
|
));
|
|
// En passant capture
|
|
if (enPassant.isEnPassantCapture(moving, fromX, toX, toY, board)) {
|
|
int capturedY = moving.isWhite() ? toY + 1 : toY - 1;
|
|
board[capturedY][toX] = null; // Remove captured pawn
|
|
}
|
|
|
|
board[toY][toX] = new Piece(toX, toY, moving.getType(), moving.isWhite());
|
|
board[fromY][fromX] = null;
|
|
|
|
enPassant.updateTarget(moving, fromY, toY, toX);
|
|
}
|
|
|
|
public Set<String> getValidMoves(Piece piece, boolean skipKingCheck) {
|
|
Set<String> validMoves = new HashSet<>();
|
|
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
PieceType type = piece.getType();
|
|
|
|
int direction = isWhite ? -1 : 1;
|
|
|
|
switch (type) {
|
|
case Pawn:
|
|
if (inBounds(x, y + direction) && board[y + direction][x] == null) {
|
|
validMoves.add(x + "," + (y + direction));
|
|
}
|
|
|
|
boolean onStartRow = (isWhite && y == 6) || (!isWhite && y == 1);
|
|
if (onStartRow && board[y + direction][x] == null && board[y + 2 * direction][x] == null) {
|
|
validMoves.add(x + "," + (y + 2 * direction));
|
|
}
|
|
|
|
int[] dx = {-1, 1};
|
|
for (int i : dx) {
|
|
int targetX = x + i;
|
|
int targetY = y + direction;
|
|
if (inBounds(targetX, targetY)) {
|
|
Piece target = board[targetY][targetX];
|
|
if (target != null && target.isWhite() != isWhite) {
|
|
validMoves.add(targetX + "," + targetY);
|
|
}
|
|
}
|
|
}
|
|
|
|
int[] epTarget = enPassant.getTarget();
|
|
if (epTarget != null && Math.abs(epTarget[0] - x) == 1 && epTarget[1] == y + direction) {
|
|
validMoves.add(epTarget[0] + "," + epTarget[1]);
|
|
}
|
|
|
|
break;
|
|
|
|
case Rook:
|
|
addLinearMoves(validMoves, x, y, isWhite, 1, 0);
|
|
addLinearMoves(validMoves, x, y, isWhite, -1, 0);
|
|
addLinearMoves(validMoves, x, y, isWhite, 0, 1);
|
|
addLinearMoves(validMoves, x, y, isWhite, 0, -1);
|
|
break;
|
|
|
|
case Bishop:
|
|
addLinearMoves(validMoves, x, y, isWhite, 1, 1);
|
|
addLinearMoves(validMoves, x, y, isWhite, 1, -1);
|
|
addLinearMoves(validMoves, x, y, isWhite, -1, 1);
|
|
addLinearMoves(validMoves, x, y, isWhite, -1, -1);
|
|
break;
|
|
|
|
case Queen:
|
|
int[][] queenDirections = {
|
|
{1, 0}, {-1, 0}, {0, 1}, {0, -1},
|
|
{1, 1}, {1, -1}, {-1, 1}, {-1, -1}
|
|
};
|
|
for (int[] dir : queenDirections) {
|
|
addLinearMoves(validMoves, x, y, isWhite, dir[0], dir[1]);
|
|
}
|
|
break;
|
|
|
|
case Knight:
|
|
int[][] knightMoves = {
|
|
{1, 2}, {2, 1}, {-1, 2}, {-2, 1},
|
|
{1, -2}, {2, -1}, {-1, -2}, {-2, -1}
|
|
};
|
|
for (int[] move : knightMoves) {
|
|
int nx = x + move[0];
|
|
int ny = y + move[1];
|
|
if (inBounds(nx, ny)) {
|
|
Piece target = board[ny][nx];
|
|
if (target == null || target.isWhite() != isWhite) {
|
|
validMoves.add(nx + "," + ny);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
case King:
|
|
for (int dxKing = -1; dxKing <= 1; dxKing++) {
|
|
for (int dyKing = -1; dyKing <= 1; dyKing++) {
|
|
if (dxKing == 0 && dyKing == 0) continue;
|
|
int nx = x + dxKing;
|
|
int ny = y + dyKing;
|
|
if (inBounds(nx, ny)) {
|
|
Piece target = board[ny][nx];
|
|
if (target == null || target.isWhite() != isWhite) {
|
|
validMoves.add(nx + "," + ny);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Castling castlingHandler = new Castling(this);
|
|
for (int rookX : new int[]{0, 7}) {
|
|
if (castlingHandler.canCastle(piece, rookX, y)) {
|
|
int newKingX = (rookX > x) ? x + 2 : x - 2;
|
|
validMoves.add(newKingX + "," + y);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
if (skipKingCheck) {
|
|
return validMoves;
|
|
} else {
|
|
return CheckKing.filterLegalMoves(this, piece, validMoves);
|
|
}
|
|
}
|
|
|
|
|
|
private void addLinearMoves(Set<String> valid, int x, int y, boolean isWhite, int dx, int dy) {
|
|
int nx = x + dx;
|
|
int ny = y + dy;
|
|
|
|
while (inBounds(nx, ny)) {
|
|
Piece target = board[ny][nx];
|
|
|
|
if (target == null) {
|
|
|
|
valid.add(nx + "," + ny);
|
|
} else {
|
|
|
|
if (target.isWhite() != isWhite) {
|
|
valid.add(nx + "," + ny);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
nx += dx;
|
|
ny += dy;
|
|
}
|
|
}
|
|
private Set<String> getValidMoves(Piece piece) {
|
|
return getValidMoves(piece, false);
|
|
}
|
|
|
|
private boolean inBounds(int x, int y) {
|
|
return x >= 0 && y >= 0 && x < colNum && y < lineNum;
|
|
}
|
|
|
|
// PART3
|
|
public String[] toFileRep() {
|
|
String[] result = new String[lineNum + 1];
|
|
for (int y = 0; y < lineNum; y++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int x = 0; x < colNum; x++) {
|
|
if (x > 0) sb.append(",");
|
|
Piece piece = board[y][x];
|
|
if (piece != null) {
|
|
sb.append(piece.isWhite() ? "W" : "B");
|
|
sb.append(piece.getType().getSummary()); // Ensure this returns K/Q/R/N/B/P
|
|
}
|
|
}
|
|
result[y] = sb.toString();
|
|
}
|
|
result[lineNum] = isWhiteTurn ? "W" : "B";
|
|
return result;
|
|
}
|
|
|
|
|
|
public Board(String[] array) {
|
|
this.colNum = 8;
|
|
this.lineNum = 8;
|
|
this.board = new Piece[lineNum][colNum];
|
|
|
|
for (int y = 0; y < lineNum; y++) {
|
|
String[] tokens = array[y].split(",", -1);
|
|
for (int x = 0; x < colNum; x++) {
|
|
String token = tokens[x].trim();
|
|
if (token.length() == 2) {
|
|
boolean isWhite = token.charAt(0) == 'W';
|
|
char typeChar = token.charAt(1);
|
|
PieceType type = PieceType.fromSummary(typeChar); // You must implement this method
|
|
board[y][x] = new Piece(x, y, type, isWhite);
|
|
}
|
|
}
|
|
}
|
|
this.isWhiteTurn = array[lineNum].equalsIgnoreCase("W");
|
|
}
|
|
|
|
|
|
public void undoLastMove() {
|
|
if (moveHistory.isEmpty()) return;
|
|
|
|
MoveRecord last = moveHistory.pop();
|
|
|
|
this.turnNumber = last.turnNumber;
|
|
this.isWhiteTurn = last.wasWhiteTurn;
|
|
enPassant.setTarget(last.enPassantTarget);
|
|
|
|
board[last.fromY][last.fromX] = new Piece(
|
|
last.fromX, last.fromY, last.movedPiece.getType(), last.movedPiece.isWhite()
|
|
);
|
|
board[last.toY][last.toX] = null;
|
|
|
|
// Restore captured piece
|
|
if (last.capturedPiece != null) {
|
|
int cx = last.capturedPiece.getX();
|
|
int cy = last.capturedPiece.getY();
|
|
|
|
// Check for en passant capture
|
|
if (last.movedPiece.getType() == PieceType.Pawn && cy != last.toY) {
|
|
// En passant capture happened — captured piece is not at (toX, toY)
|
|
board[cy][cx] = new Piece(cx, cy, last.capturedPiece.getType(), last.capturedPiece.isWhite());
|
|
} else {
|
|
board[cy][cx] = new Piece(cx, cy, last.capturedPiece.getType(), last.capturedPiece.isWhite());
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public Board(Board other) {
|
|
this.colNum = other.colNum;
|
|
this.lineNum = other.lineNum;
|
|
this.board = new Piece[lineNum][colNum];
|
|
|
|
for (int y = 0; y < lineNum; y++) {
|
|
for (int x = 0; x < colNum; x++) {
|
|
Piece p = other.board[y][x];
|
|
if (p != null) {
|
|
this.board[y][x] = new Piece(p.getX(), p.getY(), p.getType(), p.isWhite());
|
|
}
|
|
}
|
|
}
|
|
|
|
this.isWhiteTurn = other.isWhiteTurn;
|
|
this.turnNumber = other.turnNumber;
|
|
this.enPassant = new EnPassant(); // you can improve this if EnPassant has a copy constructor
|
|
}
|
|
|
|
public void playMove(Move move) {
|
|
movePiece(move.getFromX(), move.getFromY(), move.getToX(), move.getToY());
|
|
isWhiteTurn = !isWhiteTurn;
|
|
turnNumber++;
|
|
}
|
|
private class MoveRecord {
|
|
final Piece movedPiece;
|
|
final Piece capturedPiece;
|
|
final int fromX, fromY, toX, toY;
|
|
final int turnNumber;
|
|
final boolean wasWhiteTurn;
|
|
final int[] enPassantTarget;
|
|
|
|
MoveRecord(Piece movedPiece, Piece capturedPiece, int fromX, int fromY, int toX, int toY,
|
|
int turnNumber, boolean wasWhiteTurn, int[] enPassantTarget) {
|
|
this.movedPiece = movedPiece;
|
|
this.capturedPiece = capturedPiece;
|
|
this.fromX = fromX;
|
|
this.fromY = fromY;
|
|
this.toX = toX;
|
|
this.toY = toY;
|
|
this.turnNumber = turnNumber;
|
|
this.wasWhiteTurn = wasWhiteTurn;
|
|
this.enPassantTarget = enPassantTarget == null ? null : new int[]{enPassantTarget[0], enPassantTarget[1]};
|
|
}
|
|
}
|
|
}
|
|
|
|
|