OOP_3B4_Project/src/backend/Board.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]};
}
}
}