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 moveHistory = new Stack<>(); private EnPassant enPassant = new EnPassant(); private int turnNumber = 0; private boolean isWhiteTurn = true; private Piece selectedPiece = null; private Set highlightedPositions = new HashSet<>(); public Board(int colNum, int lineNum) { this.colNum = colNum; this.lineNum = lineNum; this.board = new Piece[lineNum][colNum]; } public Set getAllMoves(boolean isWhite) { Set 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 getPieces() { ArrayList 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 getValidMoves(Piece piece, boolean skipKingCheck) { Set 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 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 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]}; } } }