OOP_3A5_Project/src/backend/Board.java

1114 lines
41 KiB
Java

package backend;
import java.util.ArrayList;
public class Board {
//Board dimensions
private int width;
private int height;
private ArrayList<Piece> pieces = new ArrayList<>();
private int turnNumber = 0;
private boolean isTurnWhite = true;
private int selectedX = -1;
private int selectedY = -1;
private ArrayList<int[]> 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<Move> 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<int[]> 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<int[]> getRawValidMoves(Piece piece) {
ArrayList<int[]> 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<int[]> getValidMoves(Piece piece) {
ArrayList<int[]> rawMoves = getRawValidMoves(piece);
ArrayList<int[]> 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<Piece> 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<Piece> 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<int[]> 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<Piece> 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<int[]> 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<int[]> 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<int[]> 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;
}
}