package backend; import java.util.ArrayList; public class Board { //Board dimensions private int width; private int height; private ArrayList pieces = new ArrayList<>(); private int turnNumber = 0; private boolean isTurnWhite = true; private int selectedX = -1; private int selectedY = -1; private ArrayList highlightedPositions = new ArrayList<>(); private int enPassantX = -1; private int enPassantY = -1; private boolean whiteKingMoved = false; private boolean blackKingMoved = false; private boolean whiteRookKingsideMoved = false; private boolean whiteRookQueensideMoved = false; private boolean blackRookKingsideMoved = false; private boolean blackRookQueensideMoved = false; private boolean isInCheckState = false; private boolean isCheckmateState = false; private ArrayList moveHistory = new ArrayList<>(); public Piece getPieceAt(int x, int y) { for (Piece piece : pieces) { if (piece.getX() == x && piece.getY() == y) { return piece; } } return null; } private boolean isInitialRookPosition(int x, int y, boolean isWhite) { int baseRow = isWhite ? 7 : 0; return (y == baseRow && (x == 0 || x == 7)); } private boolean isSquareAttacked(int x, int y, boolean byWhite) { for (Piece piece : pieces) { if (piece.isWhite() == byWhite) { ArrayList moves = getRawValidMoves(piece); for (int[] move : moves) { if (move[0] == x && move[1] == y) { return true; } } } } return false; } // Get the king of a specific color private Piece getKing(boolean isWhite) { for (Piece piece : pieces) { if (piece.getType() == PieceType.King && piece.isWhite() == isWhite) { return piece; } } return null; } // This gives all the raw moves without checking if they leave the king in check private ArrayList getRawValidMoves(Piece piece) { ArrayList validMoves = new ArrayList<>(); if (piece == null) return validMoves; int x = piece.getX(); int y = piece.getY(); PieceType type = piece.getType(); boolean isWhite = piece.isWhite(); switch (type) { case Pawn: int direction = isWhite ? -1 : 1; if (isPositionEmpty(x, y + direction)) { validMoves.add(new int[]{x, y + direction}); int startRow = isWhite ? 6 : 1; if (y == startRow && isPositionEmpty(x, y + 2 * direction)) { validMoves.add(new int[]{x, y + 2 * direction}); } } if (x > 0 && canCapturePieceAt(x - 1, y + direction, isWhite)) { validMoves.add(new int[]{x - 1, y + direction}); } if (x < width - 1 && canCapturePieceAt(x + 1, y + direction, isWhite)) { validMoves.add(new int[]{x + 1, y + direction}); } // En-passant moves if ((isWhite && y == 3) || (!isWhite && y == 4)) { // Check left side en-passant if (x > 0 && x - 1 == enPassantX && y + direction == enPassantY) { validMoves.add(new int[]{x - 1, y + direction}); } // Check right side en-passant if (x < width - 1 && x + 1 == enPassantX && y + direction == enPassantY) { validMoves.add(new int[]{x + 1, y + direction}); } } break; case Rook: addMovesInDirection(validMoves, x, y, 1, 0, isWhite); addMovesInDirection(validMoves, x, y, -1, 0, isWhite); addMovesInDirection(validMoves, x, y, 0, -1, isWhite); addMovesInDirection(validMoves, x, y, 0, 1, isWhite); break; case Knight: int[][] knightMoves = { {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1} }; for (int[] move : knightMoves) { int newX = x + move[0]; int newY = y + move[1]; if (isValidPosition(newX, newY) && (isPositionEmpty(newX, newY) || canCapturePieceAt(newX, newY, isWhite))) { validMoves.add(new int[]{newX, newY}); } } break; case Bishop: addMovesInDirection(validMoves, x, y, 1, -1, isWhite); addMovesInDirection(validMoves, x, y, -1, -1, isWhite); addMovesInDirection(validMoves, x, y, 1, 1, isWhite); addMovesInDirection(validMoves, x, y, -1, 1, isWhite); break; case Queen: addMovesInDirection(validMoves, x, y, 1, 0, isWhite); addMovesInDirection(validMoves, x, y, -1, 0, isWhite); addMovesInDirection(validMoves, x, y, 0, -1, isWhite); addMovesInDirection(validMoves, x, y, 0, 1, isWhite); addMovesInDirection(validMoves, x, y, 1, -1, isWhite); addMovesInDirection(validMoves, x, y, -1, -1, isWhite); addMovesInDirection(validMoves, x, y, 1, 1, isWhite); addMovesInDirection(validMoves, x, y, -1, 1, isWhite); break; case King: for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { if (dx == 0 && dy == 0) continue; int newX = x + dx; int newY = y + dy; if (isValidPosition(newX, newY) && (isPositionEmpty(newX, newY) || canCapturePieceAt(newX, newY, isWhite))) { validMoves.add(new int[]{newX, newY}); } } } // Check castling possibilities int baseRow = isWhite ? 7 : 0; if (y == baseRow && x == 4) { boolean kingMoved = isWhite ? whiteKingMoved : blackKingMoved; if (!kingMoved) { // Kingside castling boolean rookKingsideMoved = isWhite ? whiteRookKingsideMoved : blackRookKingsideMoved; if (!rookKingsideMoved && isPositionEmpty(5, baseRow) && isPositionEmpty(6, baseRow)) { // Check if king would pass through check if (!isSquareAttacked(4, baseRow, !isWhite) && !isSquareAttacked(5, baseRow, !isWhite) && !isSquareAttacked(6, baseRow, !isWhite)) { validMoves.add(new int[]{6, baseRow}); } } // Queenside castling boolean rookQueensideMoved = isWhite ? whiteRookQueensideMoved : blackRookQueensideMoved; if (!rookQueensideMoved && isPositionEmpty(3, baseRow) && isPositionEmpty(2, baseRow) && isPositionEmpty(1, baseRow)) { // Check if king would pass through check if (!isSquareAttacked(4, baseRow, !isWhite) && !isSquareAttacked(3, baseRow, !isWhite) && !isSquareAttacked(2, baseRow, !isWhite)) { validMoves.add(new int[]{2, baseRow}); } } } } break; } return validMoves; } // Modified getValidMoves that filters moves that would leave the king in check private ArrayList getValidMoves(Piece piece) { ArrayList rawMoves = getRawValidMoves(piece); ArrayList legalMoves = new ArrayList<>(); for (int[] move : rawMoves) { // Simulate the move and check if king would be in check if (!wouldKingBeInCheck(piece, move[0], move[1])) { legalMoves.add(move); } } return legalMoves; } // Check if a move would leave the king in check private boolean wouldKingBeInCheck(Piece pieceToMove, int newX, int newY) { // Create a temporary copy of the board to simulate the move ArrayList originalPieces = new ArrayList<>(pieces); // Find and remove the piece at the destination if it exists (capture) Piece capturedPiece = null; for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == newX && piece.getY() == newY) { capturedPiece = piece; pieces.remove(i); break; } } // Handle en passant capture in the simulation if (pieceToMove.getType() == PieceType.Pawn && newX == enPassantX && newY == enPassantY) { for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == newX && piece.getY() == pieceToMove.getY()) { capturedPiece = piece; pieces.remove(i); break; } } } // Update the position of the moved piece temporarily int originalX = pieceToMove.getX(); int originalY = pieceToMove.getY(); // Remove the original piece for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == originalX && piece.getY() == originalY) { pieces.remove(i); break; } } // Add the piece at the new position pieces.add(new Piece(newX, newY, pieceToMove.getType(), pieceToMove.isWhite())); // Find the king after the move Piece king = getKing(pieceToMove.isWhite()); // Check if the king would be in check boolean kingInCheck = king != null && isSquareAttacked(king.getX(), king.getY(), !pieceToMove.isWhite()); // Restore the board to its original state pieces = originalPieces; return kingInCheck; } public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; this.turnNumber = 0; this.isTurnWhite = true; } public int getWidth() { return width; } public int getHeight() { return height; } public int getTurnNumber() { return turnNumber; } public boolean isTurnWhite() { return isTurnWhite; } public boolean isInCheckState() { return isInCheckState; } public boolean isCheckmateState() { return isCheckmateState; } public void setPiece(boolean isWhite, PieceType type, int x, int y) { if (x < 0 || x >= width || y < 0 || y >= height) { return; } for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == x && piece.getY() == y) { pieces.remove(i); break; } } pieces.add(new Piece(x, y, type, isWhite)); } public void populateBoard() { cleanBoard(); for (int x = 0; x < getWidth(); x++) { setPiece(true, PieceType.Pawn, x, 6); setPiece(false, PieceType.Pawn, x, 1); } setPiece(true, PieceType.Rook, 0, 7); setPiece(true, PieceType.Rook, 7, 7); setPiece(false, PieceType.Rook, 0, 0); setPiece(false, PieceType.Rook, 7, 0); setPiece(true, PieceType.Knight, 1, 7); setPiece(true, PieceType.Knight, 6, 7); setPiece(false, PieceType.Knight, 1, 0); setPiece(false, PieceType.Knight, 6, 0); setPiece(true, PieceType.Bishop, 2, 7); setPiece(true, PieceType.Bishop, 5, 7); setPiece(false, PieceType.Bishop, 2, 0); setPiece(false, PieceType.Bishop, 5, 0); setPiece(true, PieceType.Queen, 3, 7); setPiece(false, PieceType.Queen, 3, 0); setPiece(true, PieceType.King, 4, 7); setPiece(false, PieceType.King, 4, 0); whiteKingMoved = false; blackKingMoved = false; whiteRookKingsideMoved = false; whiteRookQueensideMoved = false; blackRookKingsideMoved = false; blackRookQueensideMoved = false; enPassantX = -1; enPassantY = -1; // Reset turn number and turn color turnNumber = 0; isTurnWhite = true; // Clear move history moveHistory.clear(); } public void cleanBoard() { pieces.clear(); whiteKingMoved = false; blackKingMoved = false; whiteRookKingsideMoved = false; whiteRookQueensideMoved = false; blackRookKingsideMoved = false; blackRookQueensideMoved = false; enPassantX = -1; enPassantY = -1; } public String toString() { StringBuilder result = new StringBuilder(); char[][] board = new char[height][width]; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { board[y][x] = '.'; } } for (Piece piece : pieces) { char symbol; switch (piece.getType()) { case Pawn: symbol = 'P'; break; case Rook: symbol = 'R'; break; case Knight: symbol = 'N'; break; case Bishop: symbol = 'B'; break; case Queen: symbol = 'Q'; break; case King: symbol = 'K'; break; default: symbol = '?'; } if (!piece.isWhite()) { symbol = Character.toLowerCase(symbol); } board[piece.getY()][piece.getX()] = symbol; } for (int y = 0; y < height; y++) { result.append(8 - y).append(" "); for (int x = 0; x < width; x++) { result.append(board[y][x]).append(" "); } result.append("\n"); } result.append(" "); for (int x = 0; x < width; x++) { result.append((char)('a' + x)).append(" "); } return result.toString(); } public ArrayList getPieces() { return new ArrayList<>(pieces); } public void userTouch(int x, int y) { // Check if coordinates are within board boundaries if (x < 0 || x >= width || y < 0 || y >= height) { return; } Piece touchedPiece = getPieceAt(x, y); // Case 1: No piece is currently selected if (selectedX == -1 && selectedY == -1) { // Only select a piece if it exists and belongs to the current player if (touchedPiece != null && touchedPiece.isWhite() == isTurnWhite) { selectedX = x; selectedY = y; // Calculate and highlight valid moves for the selected piece highlightedPositions.clear(); ArrayList validMoves = getValidMoves(touchedPiece); // Only highlight if the piece has valid moves highlightedPositions.addAll(validMoves); } } // Case 2: A piece is already selected else { Piece selectedPiece = getPieceAt(selectedX, selectedY); // Case 2.1: Player clicked on the already selected piece (deselect it) if (selectedX == x && selectedY == y) { selectedX = -1; selectedY = -1; highlightedPositions.clear(); } // Case 2.2: Player clicked on another piece of the same color (change selection) else if (touchedPiece != null && touchedPiece.isWhite() == isTurnWhite) { selectedX = x; selectedY = y; highlightedPositions.clear(); highlightedPositions.addAll(getValidMoves(touchedPiece)); } // Case 2.3: Player clicked on an empty square or opponent's piece else if (selectedPiece != null) { // Check if the move is valid (among highlighted positions) boolean moveIsValid = false; for (int[] pos : highlightedPositions) { if (pos[0] == x && pos[1] == y) { moveIsValid = true; break; } } // If the move is valid, execute it if (moveIsValid) { // Store captured piece (if any) for move history Piece capturedPiece = getPieceAt(x, y); boolean isEnPassantCapture = false; boolean isCastling = false; boolean isPawnPromotion = false; PieceType promotionType = null; // Check for pawn promotion before making the move if (selectedPiece.getType() == PieceType.Pawn && ((selectedPiece.isWhite() && y == 0) || (!selectedPiece.isWhite() && y == 7))) { isPawnPromotion = true; // In a real implementation, you would prompt the user here // For now, default to Queen (this could be modified to take input) promotionType = PieceType.Queen; } // Handle special cases for King movement if (selectedPiece.getType() == PieceType.King) { // Update king movement flags if (selectedPiece.isWhite()) { whiteKingMoved = true; } else { blackKingMoved = true; } // Check for castling (king moves 2 squares horizontally) if (Math.abs(x - selectedX) == 2 && y == selectedY) { isCastling = true; // Determine rook's original and new positions int rookX = (x > selectedX) ? 7 : 0; int newRookX = (x > selectedX) ? x - 1 : x + 1; // Move the rook Piece rook = getPieceAt(rookX, y); if (rook != null && rook.getType() == PieceType.Rook) { // Remove rook from original position removePieceAt(rookX, y); // Place rook at new position setPiece(selectedPiece.isWhite(), PieceType.Rook, newRookX, y); } } } // Handle special cases for Rook movement (for castling flags) else if (selectedPiece.getType() == PieceType.Rook) { if (isInitialRookPosition(selectedX, selectedY, selectedPiece.isWhite())) { if (selectedPiece.isWhite()) { if (selectedX == 0) { whiteRookQueensideMoved = true; } else if (selectedX == 7) { whiteRookKingsideMoved = true; } } else { if (selectedX == 0) { blackRookQueensideMoved = true; } else if (selectedX == 7) { blackRookKingsideMoved = true; } } } } // Handle En Passant setup // Reset en passant by default int oldEnPassantX = enPassantX; int oldEnPassantY = enPassantY; enPassantX = -1; enPassantY = -1; // If a pawn moves 2 squares, record position for potential en passant if (selectedPiece.getType() == PieceType.Pawn && Math.abs(y - selectedY) == 2) { enPassantX = x; enPassantY = selectedPiece.isWhite() ? y + 1 : y - 1; } // Check for en passant capture // When a pawn moves diagonally to an empty square, it might be capturing en passant if (selectedPiece.getType() == PieceType.Pawn && x != selectedX && getPieceAt(x, y) == null && x == oldEnPassantX && y == oldEnPassantY) { Piece possiblePawn = getPieceAt(x, selectedY); if (possiblePawn != null && possiblePawn.getType() == PieceType.Pawn && possiblePawn.isWhite() != selectedPiece.isWhite()) { isEnPassantCapture = true; capturedPiece = possiblePawn; removePieceAt(x, selectedY); } } // Create move record before actually moving the pieces Move move = new Move(selectedX, selectedY, x, y, new Piece(selectedX, selectedY, selectedPiece.getType(), selectedPiece.isWhite()), capturedPiece, isEnPassantCapture, isCastling); moveHistory.add(move); // Now move the piece if (isPawnPromotion) { // For promotion, place the promoted piece setPiece(selectedPiece.isWhite(), promotionType, x, y); } else { // Standard move setPiece(selectedPiece.isWhite(), selectedPiece.getType(), x, y); } // Remove the piece from original position removePieceAt(selectedX, selectedY); // Update turn info turnNumber++; isTurnWhite = !isTurnWhite; // Vérifier si le roi est en échec isInCheckState = isInCheck(isTurnWhite); // Vérifier si c'est un échec et mat ou un pat if (isInCheckState) { isCheckmateState = isCheckmate(); if (isCheckmateState) { System.out.println("Checkmate! " + (!isTurnWhite ? "White" : "Black") + " wins!"); } else { System.out.println("Check8 " + (isTurnWhite ? "white" : "Black") + "!"); } } else if (isStalemate()) { System.out.println("Stalemate! Game is a draw."); } } // Reset selection regardless of whether move was valid selectedX = -1; selectedY = -1; highlightedPositions.clear(); } } } private void removePieceAt(int x, int y) { for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == x && piece.getY() == y) { pieces.remove(i); break; } } } public boolean isCheckmate() { boolean currentPlayerIsWhite = isTurnWhite; // If king is in check and no legal moves exist return isInCheck(currentPlayerIsWhite) && !hasLegalMoves(currentPlayerIsWhite); } public boolean isStalemate() { boolean currentPlayerIsWhite = isTurnWhite; // If king is NOT in check but no legal moves exist return !isInCheck(currentPlayerIsWhite) && !hasLegalMoves(currentPlayerIsWhite); } private boolean hasLegalMoves(boolean isWhite) { // Créer une copie de la liste pour éviter les modifications concurrentes ArrayList piecesToCheck = new ArrayList<>(pieces); for (Piece piece : piecesToCheck) { if (piece.isWhite() == isWhite && !getValidMoves(piece).isEmpty()) { return true; } } return false; } public boolean isInCheck(boolean isWhite) { Piece king = getKing(isWhite); return king != null && isSquareAttacked(king.getX(), king.getY(), !isWhite); } public boolean isSelected(int x, int y) { return selectedX == x && selectedY == y; } /* saving-loading feature: */ public String[] toFileRep() { String[] fileRep = new String[height + 2]; for (int y = 0; y < height; y++) { StringBuilder row = new StringBuilder(); for (int x = 0; x < width; x++) { Piece piece = getPieceAt(x, y); if (piece == null) { row.append("--"); } else { char colorChar = piece.isWhite() ? 'W' : 'B'; char typeChar; switch (piece.getType()) { case Pawn: typeChar = 'P'; break; case Rook: typeChar = 'R'; break; case Knight: typeChar = 'N'; break; case Bishop: typeChar = 'B'; break; case Queen: typeChar = 'Q'; break; case King: typeChar = 'K'; break; default: typeChar = '?'; } row.append(colorChar).append(typeChar); } if (x < width - 1) { row.append(","); } } fileRep[y] = row.toString(); } fileRep[height] = isTurnWhite ? "W" : "B"; StringBuilder stateInfo = new StringBuilder(); stateInfo.append(whiteKingMoved ? "1" : "0"); stateInfo.append(blackKingMoved ? "1" : "0"); stateInfo.append(whiteRookKingsideMoved ? "1" : "0"); stateInfo.append(whiteRookQueensideMoved ? "1" : "0"); stateInfo.append(blackRookKingsideMoved ? "1" : "0"); stateInfo.append(blackRookQueensideMoved ? "1" : "0"); stateInfo.append(","); stateInfo.append(enPassantX).append(",").append(enPassantY); fileRep[height + 1] = stateInfo.toString(); return fileRep; } public Board(String[] fileRep) { this.width = 8; this.height = 8; if (fileRep != null && fileRep.length > 0) { this.height = fileRep.length - 1; for (int y = 0; y < height; y++) { if (y < fileRep.length) { String[] squares = fileRep[y].split(","); if (y == 0) { this.width = squares.length; } for (int x = 0; x < width && x < squares.length; x++ ) { String squareInfo = squares[x]; if (squareInfo.length() < 2 || squareInfo.equals("--")) { continue; } boolean isWhite = squareInfo.charAt(0) == 'W'; PieceType type = null; switch (squareInfo.charAt(1)) { case 'P': type = PieceType.Pawn; break; case 'R': type = PieceType.Rook; break; case 'N': type = PieceType.Knight; break; case 'B': type = PieceType.Bishop; break; case 'Q': type = PieceType.Queen; break; case 'K': type = PieceType.King; break; } if (type != null) { setPiece(isWhite, type, x, y); } } } } if (fileRep.length > height && fileRep[height] != null && fileRep[height].length() > 0) { this.isTurnWhite = fileRep[height].charAt(0) == 'W'; } else { this.isTurnWhite = true; // Default to white's turn } } this.turnNumber = 0; this.selectedX = -1; this.selectedY = -1; this.highlightedPositions = new ArrayList<>(); if (fileRep.length > height + 1 && fileRep[height + 1] != null) { String[] stateInfo = fileRep[height + 1].split(","); if (stateInfo.length >= 1 && stateInfo[0].length() >= 6) { whiteKingMoved = stateInfo[0].charAt(0) == '1'; blackKingMoved = stateInfo[0].charAt(1) == '1'; whiteRookKingsideMoved = stateInfo[0].charAt(2) == '1'; whiteRookQueensideMoved = stateInfo[0].charAt(3) == '1'; blackRookKingsideMoved = stateInfo[0].charAt(4) == '1'; blackRookQueensideMoved = stateInfo[0].charAt(5) == '1'; } if (stateInfo.length >= 3) { try { enPassantX = Integer.parseInt(stateInfo[1]); enPassantY = Integer.parseInt(stateInfo[2]); } catch (NumberFormatException e) { enPassantX = -1; enPassantY = -1; } } } } public boolean isHighlighted(int x, int y) { for (int[] pos : highlightedPositions) { if (pos[0] == x && pos[1] == y) { return true; } } return false; } public void undoLastMove() { if (moveHistory.isEmpty()) { return; // Rien à annuler } // Récupérer le dernier mouvement Move lastMove = moveHistory.get(moveHistory.size() - 1); moveHistory.remove(moveHistory.size() - 1); // Restaurer les pièces à leurs positions d'origine int fromX = lastMove.getFromX(); int fromY = lastMove.getFromY(); int toX = lastMove.getToX(); int toY = lastMove.getToY(); Piece movingPiece = lastMove.getMovingPiece(); Piece capturedPiece = lastMove.getCapturedPiece(); boolean isEnPassantCapture = lastMove.isEnPassantCapture(); boolean isCastling = lastMove.isCastling(); // Retirer la pièce déplacée de sa position actuelle removePieceAt(toX, toY); // Remettre la pièce déplacée à sa position d'origine setPiece(movingPiece.isWhite(), movingPiece.getType(), fromX, fromY); // Restaurer la pièce capturée s'il y en avait une if (capturedPiece != null) { if (isEnPassantCapture) { // Pour l'en passant, restaurer le pion à sa position originale setPiece(capturedPiece.isWhite(), capturedPiece.getType(), toX, fromY); } else { // Pour une capture normale, restaurer la pièce à l'emplacement de capture setPiece(capturedPiece.isWhite(), capturedPiece.getType(), toX, toY); } } // Gérer l'annulation du roque if (isCastling) { // Déterminer les positions originales et déplacées de la tour int rookOriginalX = (toX > fromX) ? 7 : 0; int rookMovedX = (toX > fromX) ? toX - 1 : toX + 1; // Retirer la tour de sa position actuelle removePieceAt(rookMovedX, toY); // Remettre la tour à sa position d'origine setPiece(movingPiece.isWhite(), PieceType.Rook, rookOriginalX, toY); } // Restaurer l'état précédent d'en passant et des drapeaux de roque // Ceci nécessite soit de stocker l'état précédent dans l'objet Move, // soit de recalculer à partir de l'historique restant resetMoveFlags(); // Décrémenter le numéro de tour et changer le joueur actif turnNumber--; isTurnWhite = !isTurnWhite; // Réinitialiser la sélection et les cases surlignées selectedX = -1; selectedY = -1; highlightedPositions.clear(); } public Board(Board board) { this.width = board.width; this.height = board.height; this.turnNumber = board.turnNumber; this.isTurnWhite = board.isTurnWhite; this.selectedX = -1; this.selectedY = -1; this.highlightedPositions = new ArrayList<>(); this.enPassantX = board.enPassantX; this.enPassantY = board.enPassantY; this.whiteKingMoved = board.whiteKingMoved; this.blackKingMoved = board.blackKingMoved; this.whiteRookKingsideMoved = board.whiteRookKingsideMoved; this.whiteRookQueensideMoved = board.whiteRookQueensideMoved; this.blackRookKingsideMoved = board.blackRookKingsideMoved; this.blackRookQueensideMoved = board.blackRookQueensideMoved; // Create deep copies of all pieces this.pieces = new ArrayList<>(); for (Piece piece : board.pieces) { this.pieces.add(new Piece(piece.getX(), piece.getY(), piece.getType(), piece.isWhite())); } // Create deep copies of move history this.moveHistory = new ArrayList<>(); for (Move move : board.moveHistory) { this.moveHistory.add(move); // Move objects are immutable, so direct reference is fine } //TODO } public void playMove(Move move) { int fromX = move.getFromX(); int fromY = move.getFromY(); int toX = move.getToX(); int toY = move.getToY(); Piece pieceToMove = getPieceAt(fromX, fromY); if (pieceToMove == null) return; // Store captured piece (if any) for move history Piece capturedPiece = getPieceAt(toX, toY); boolean isEnPassantCapture = move.isEnPassantCapture(); boolean isCastling = move.isCastling(); // Update king movement flags if (pieceToMove.getType() == PieceType.King) { if (pieceToMove.isWhite()) { whiteKingMoved = true; } else { blackKingMoved = true; } // Handle castling move if (isCastling) { // Determine rook position and new position int rookX = (toX > fromX) ? 7 : 0; int newRookX = (toX > fromX) ? toX - 1 : toX + 1; // Move the rook too Piece rook = getPieceAt(rookX, toY); if (rook != null && rook.getType() == PieceType.Rook) { for (int i = 0; i < pieces.size(); i++) { if (pieces.get(i).getX() == rookX && pieces.get(i).getY() == toY) { pieces.remove(i); break; } } setPiece(pieceToMove.isWhite(), PieceType.Rook, newRookX, toY); } } } // Update rook movement flags else if (pieceToMove.getType() == PieceType.Rook) { if (isInitialRookPosition(fromX, fromY, pieceToMove.isWhite())) { if (pieceToMove.isWhite()) { if (fromX == 0) { whiteRookQueensideMoved = true; } else { whiteRookKingsideMoved = true; } } else { if (fromX == 0) { blackRookQueensideMoved = true; } else { blackRookKingsideMoved = true; } } } } // Handle en passant setup if (pieceToMove.getType() == PieceType.Pawn && Math.abs(toY - fromY) == 2) { enPassantX = toX; enPassantY = pieceToMove.isWhite() ? toY + 1 : toY - 1; } else { enPassantX = -1; enPassantY = -1; } // Handle en passant capture if (isEnPassantCapture) { // Remove the pawn being captured by en-passant for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == toX && piece.getY() == fromY) { pieces.remove(i); break; } } } // Store move in history if (!moveHistory.contains(move)) { Move historyMove = new Move(fromX, fromY, toX, toY, new Piece(fromX, fromY, pieceToMove.getType(), pieceToMove.isWhite()), capturedPiece, isEnPassantCapture, isCastling); moveHistory.add(historyMove); } // Move the piece setPiece(pieceToMove.isWhite(), pieceToMove.getType(), toX, toY); // Remove piece from original position for (int i = 0; i < pieces.size(); i++) { Piece piece = pieces.get(i); if (piece.getX() == fromX && piece.getY() == fromY) { pieces.remove(i); break; } } turnNumber++; isTurnWhite = !isTurnWhite; //TODO } private void resetMoveFlags() { // Reset all flags whiteKingMoved = false; blackKingMoved = false; whiteRookKingsideMoved = false; whiteRookQueensideMoved = false; blackRookKingsideMoved = false; blackRookQueensideMoved = false; // Set en passant properly enPassantX = -1; enPassantY = -1; // If we have at least one move left in history, we need to check // for potential en passant setup from the last move if (!moveHistory.isEmpty()) { Move lastMove = moveHistory.get(moveHistory.size() - 1); Piece movingPiece = lastMove.getMovingPiece(); int fromY = lastMove.getFromY(); int toY = lastMove.getToY(); int toX = lastMove.getToX(); if (movingPiece.getType() == PieceType.Pawn && Math.abs(toY - fromY) == 2) { enPassantX = toX; enPassantY = movingPiece.isWhite() ? toY + 1 : toY - 1; } } // For each move in history, update the flags for (Move move : moveHistory) { Piece movingPiece = move.getMovingPiece(); int fromX = move.getFromX(); int fromY = move.getFromY(); // Check king moves if (movingPiece.getType() == PieceType.King) { if (movingPiece.isWhite()) { whiteKingMoved = true; } else { blackKingMoved = true; } } // Check rook moves else if (movingPiece.getType() == PieceType.Rook) { boolean isWhite = movingPiece.isWhite(); int baseRow = isWhite ? 7 : 0; if (fromY == baseRow) { if (fromX == 0) { if (isWhite) { whiteRookQueensideMoved = true; } else { blackRookQueensideMoved = true; } } else if (fromX == 7) { if (isWhite) { whiteRookKingsideMoved = true; } else { blackRookKingsideMoved = true; } } } } } } private void addMovesInDirection(ArrayList moves, int x, int y, int dx, int dy, boolean isWhite) { int newX = x + dx; int newY = y + dy; while (isValidPosition(newX, newY)) { if (isPositionEmpty(newX, newY)) { moves.add(new int[]{newX, newY}); newX += dx; newY += dy; } else if (canCapturePieceAt(newX, newY, isWhite)) { moves.add(new int[]{newX, newY}); break; } else { break; } } } private boolean isPositionEmpty(int x, int y) { return getPieceAt(x, y) == null; } private boolean isValidPosition(int x, int y) { return x >= 0 && x < width && y >= 0 && y < height; } private boolean canCapturePieceAt(int x, int y, boolean isWhite) { Piece pieceToCapture = getPieceAt(x, y); return pieceToCapture != null && pieceToCapture.isWhite() != isWhite; } public ArrayList getValidMovesForPiece(int x, int y) { Piece piece = getPieceAt(x, y); if (piece != null && piece.isWhite() == isTurnWhite) { return getValidMoves(piece); } return new ArrayList<>(); } public boolean makeMove(int fromX, int fromY, int toX, int toY) { Piece piece = getPieceAt(fromX, fromY); if (piece == null || piece.isWhite() != isTurnWhite) { return false; } // Check if move is valid boolean moveIsValid = false; ArrayList validMoves = getValidMoves(piece); for (int[] pos : validMoves) { if (pos[0] == toX && pos[1] == toY) { moveIsValid = true; break; } } if (moveIsValid) { // Execute the move logic similar to userTouch // ... return true; } return false; } }