1095 lines
40 KiB
Java
1095 lines
40 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 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 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;
|
|
|
|
// Check for checkmate or stalemate after the move
|
|
if (isCheckmate()) {
|
|
// Game over - checkmate
|
|
// You could set a game state flag here or trigger a UI event
|
|
System.out.println("Checkmate! " + (isTurnWhite ? "Black" : "White") + " wins!");
|
|
} else if (isStalemate()) {
|
|
// Game over - stalemate
|
|
// You could set a game state flag here or trigger a UI event
|
|
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) {
|
|
for (Piece piece : pieces) {
|
|
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;
|
|
}
|
|
|
|
}
|