485 lines
19 KiB
Java
485 lines
19 KiB
Java
package backend;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
public class Move {
|
|
private int fromX;
|
|
private int fromY;
|
|
private int toX;
|
|
private int toY;
|
|
private Piece movingPiece;
|
|
private Piece capturedPiece;
|
|
private Board board;
|
|
|
|
// Castling-related fields
|
|
private CastlingHandler castlingHandler;
|
|
private CastlingHandler.CastlingMove castlingMove;
|
|
private boolean isCastling;
|
|
|
|
// En passant-related fields
|
|
private Enpassant enpassantHandler;
|
|
private boolean isEnPassant;
|
|
|
|
public Move(int fromX, int fromY, int toX, int toY, Board board) {
|
|
this.fromX = fromX;
|
|
this.fromY = fromY;
|
|
this.toX = toX;
|
|
this.toY = toY;
|
|
this.board = board;
|
|
this.movingPiece = board.getPieceAt(fromX, fromY);
|
|
this.capturedPiece = board.getPieceAt(toX, toY);
|
|
|
|
// Initialize castling handler and check if this is a castling move
|
|
this.castlingHandler = new CastlingHandler(board);
|
|
this.castlingMove = castlingHandler.getCastlingMove(fromX, fromY, toX, toY);
|
|
this.isCastling = (castlingMove != null);
|
|
|
|
// Initialize en passant handler and check if this is an en passant move
|
|
this.enpassantHandler = new Enpassant(board);
|
|
this.isEnPassant = enpassantHandler.isEnPassantValid(fromX, fromY, toX, toY);
|
|
}
|
|
|
|
public boolean isValid() {
|
|
// Check if the moving piece exists
|
|
if (movingPiece == null) {
|
|
return false;
|
|
}
|
|
|
|
// Handle castling validation
|
|
if (isCastling) {
|
|
return castlingHandler.isCastlingValid(castlingMove);
|
|
}
|
|
|
|
// Handle en passant validation
|
|
if (isEnPassant) {
|
|
return enpassantHandler.isEnPassantValid(fromX, fromY, toX, toY);
|
|
}
|
|
|
|
// Check if the move is in the list of valid moves for this piece
|
|
List<Position> validMoves = getValidDestinations(movingPiece, board);
|
|
return containsPosition(validMoves, new Position(toX, toY));
|
|
}
|
|
|
|
public void execute() {
|
|
if (isCastling) {
|
|
// Execute castling move
|
|
castlingHandler.executeCastling(castlingMove);
|
|
} else if (isEnPassant) {
|
|
// Execute en passant capture
|
|
enpassantHandler.executeEnPassant(fromX, fromY, toX, toY);
|
|
} else {
|
|
// Remove any piece at the destination (normal capture)
|
|
if (capturedPiece != null) {
|
|
board.removePiece(toX, toY);
|
|
}
|
|
|
|
// Move the piece (remove from original position, add to new position)
|
|
board.removePiece(fromX, fromY);
|
|
board.setPiece(movingPiece.isWhite(), movingPiece.getType(), toX, toY);
|
|
}
|
|
|
|
// Handle en passant state updates for pawn moves
|
|
if (movingPiece.getType() == PieceType.Pawn && !isEnPassant) {
|
|
board.handlePawnDoubleMove(fromX, fromY, toX, toY);
|
|
} else {
|
|
// Clear en passant for non-pawn moves
|
|
board.clearEnPassant();
|
|
}
|
|
|
|
// Advance turn
|
|
board.advanceTurn();
|
|
}
|
|
|
|
public boolean putsOwnKingInCheck() {
|
|
// Create a temporary board to simulate the move
|
|
Board tempBoard = new Board(board);
|
|
|
|
// Find the piece in the temp board
|
|
Piece tempPiece = tempBoard.getPieceAt(fromX, fromY);
|
|
if (tempPiece == null) return true; // Safety check
|
|
|
|
if (isCastling) {
|
|
// For castling, we need to check the castling move specifically
|
|
CastlingHandler tempHandler = new CastlingHandler(tempBoard);
|
|
CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY);
|
|
if (tempCastling != null) {
|
|
tempHandler.executeCastling(tempCastling);
|
|
}
|
|
} else if (isEnPassant) {
|
|
// For en passant, execute the en passant capture
|
|
Enpassant tempEnpassant = new Enpassant(tempBoard);
|
|
tempEnpassant.executeEnPassant(fromX, fromY, toX, toY);
|
|
} else {
|
|
// Remove any piece at destination (normal capture)
|
|
tempBoard.removePiece(toX, toY);
|
|
|
|
// Move the piece
|
|
tempBoard.removePiece(fromX, fromY);
|
|
tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY);
|
|
}
|
|
|
|
// Check if king is in check after move
|
|
CastlingHandler tempHandler = new CastlingHandler(tempBoard);
|
|
return tempHandler.isKingInCheck(tempPiece.isWhite());
|
|
}
|
|
|
|
public boolean putsOpponentInCheck() {
|
|
// Create a temporary board to simulate the move
|
|
Board tempBoard = new Board(board);
|
|
|
|
// Find the piece in the temp board
|
|
Piece tempPiece = tempBoard.getPieceAt(fromX, fromY);
|
|
if (tempPiece == null) return false; // Safety check
|
|
|
|
if (isCastling) {
|
|
// For castling, execute the castling move
|
|
CastlingHandler tempHandler = new CastlingHandler(tempBoard);
|
|
CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY);
|
|
if (tempCastling != null) {
|
|
tempHandler.executeCastling(tempCastling);
|
|
}
|
|
} else if (isEnPassant) {
|
|
// For en passant, execute the en passant capture
|
|
Enpassant tempEnpassant = new Enpassant(tempBoard);
|
|
tempEnpassant.executeEnPassant(fromX, fromY, toX, toY);
|
|
} else {
|
|
// Remove any piece at destination (normal capture)
|
|
tempBoard.removePiece(toX, toY);
|
|
|
|
// Move the piece
|
|
tempBoard.removePiece(fromX, fromY);
|
|
tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY);
|
|
}
|
|
|
|
// Check if opponent's king is in check after move
|
|
CastlingHandler tempHandler = new CastlingHandler(tempBoard);
|
|
return tempHandler.isKingInCheck(!tempPiece.isWhite());
|
|
}
|
|
|
|
public boolean putsOpponentInCheckmate() {
|
|
// First, check if the move puts the opponent in check
|
|
if (!putsOpponentInCheck()) {
|
|
return false;
|
|
}
|
|
|
|
// Create a temporary board to simulate the move
|
|
Board tempBoard = new Board(board);
|
|
|
|
// Find the piece in the temp board
|
|
Piece tempPiece = tempBoard.getPieceAt(fromX, fromY);
|
|
if (tempPiece == null) return false; // Safety check
|
|
|
|
if (isCastling) {
|
|
// For castling, execute the castling move
|
|
CastlingHandler tempHandler = new CastlingHandler(tempBoard);
|
|
CastlingHandler.CastlingMove tempCastling = tempHandler.getCastlingMove(fromX, fromY, toX, toY);
|
|
if (tempCastling != null) {
|
|
tempHandler.executeCastling(tempCastling);
|
|
}
|
|
} else if (isEnPassant) {
|
|
// For en passant, execute the en passant capture
|
|
Enpassant tempEnpassant = new Enpassant(tempBoard);
|
|
tempEnpassant.executeEnPassant(fromX, fromY, toX, toY);
|
|
} else {
|
|
// Remove any piece at destination (normal capture)
|
|
tempBoard.removePiece(toX, toY);
|
|
|
|
// Move the piece
|
|
tempBoard.removePiece(fromX, fromY);
|
|
tempBoard.setPiece(tempPiece.isWhite(), tempPiece.getType(), toX, toY);
|
|
}
|
|
|
|
boolean opponentColor = !tempPiece.isWhite();
|
|
|
|
// Try all possible moves for all opponent pieces
|
|
for (Piece p : tempBoard.getPieces()) {
|
|
if (p.isWhite() != opponentColor) continue; // Skip pieces of the wrong color
|
|
|
|
List<Position> possibleMoves = getValidDestinations(p, tempBoard);
|
|
for (Position pos : possibleMoves) {
|
|
// Create a temporary move
|
|
Move testMove = new Move(p.getX(), p.getY(), pos.x, pos.y, tempBoard);
|
|
|
|
// Check if this move would get the opponent out of check
|
|
if (!testMove.putsOwnKingInCheck()) {
|
|
return false; // Opponent has at least one valid move
|
|
}
|
|
}
|
|
}
|
|
|
|
// If we get here, opponent has no valid moves
|
|
return true;
|
|
}
|
|
|
|
public List<Position> getValidDestinations(Piece piece, Board board) {
|
|
List<Position> moves = new ArrayList<>();
|
|
|
|
switch (piece.getType()) {
|
|
case Pawn:
|
|
addPawnMoves(moves, piece, board);
|
|
break;
|
|
case Rook:
|
|
addRookMoves(moves, piece, board);
|
|
break;
|
|
case Knight:
|
|
addKnightMoves(moves, piece, board);
|
|
break;
|
|
case Bishop:
|
|
addBishopMoves(moves, piece, board);
|
|
break;
|
|
case Queen:
|
|
addRookMoves(moves, piece, board);
|
|
addBishopMoves(moves, piece, board);
|
|
break;
|
|
case King:
|
|
addKingMoves(moves, piece, board);
|
|
break;
|
|
}
|
|
|
|
return moves;
|
|
}
|
|
|
|
public List<Position> getValidMoves(Piece piece, Board board) {
|
|
// Get all possible moves without considering check
|
|
List<Position> candidateMoves = getValidDestinations(piece, board);
|
|
|
|
// Filter out moves that would leave king in check
|
|
List<Position> legalMoves = new ArrayList<>();
|
|
for (Position pos : candidateMoves) {
|
|
Move move = new Move(piece.getX(), piece.getY(), pos.x, pos.y, board);
|
|
if (!move.putsOwnKingInCheck()) {
|
|
legalMoves.add(pos);
|
|
}
|
|
}
|
|
|
|
return legalMoves;
|
|
}
|
|
|
|
public boolean isKingInCheck(Board board, boolean isWhiteKing) {
|
|
CastlingHandler handler = new CastlingHandler(board);
|
|
return handler.isKingInCheck(isWhiteKing);
|
|
}
|
|
|
|
private void addPawnMoves(List<Position> validMoves, Piece piece, Board board) {
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
|
|
int direction = isWhite ? -1 : 1; // White pawns move up (decreasing y), black pawns move down (increasing y)
|
|
|
|
// Forward movement
|
|
int newY = y + direction;
|
|
if (newY >= 0 && newY < board.getHeight()) {
|
|
// Single square forward
|
|
if (board.getPieceAt(x, newY) == null) {
|
|
validMoves.add(new Position(x, newY));
|
|
|
|
// Initial double move
|
|
int startRow = isWhite ? 6 : 1;
|
|
if (y == startRow) {
|
|
int doubleY = newY + direction;
|
|
if (doubleY >= 0 && doubleY < board.getHeight() && board.getPieceAt(x, doubleY) == null) {
|
|
validMoves.add(new Position(x, doubleY));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Diagonal captures (regular captures)
|
|
for (int dx = -1; dx <= 1; dx += 2) {
|
|
int newX = x + dx;
|
|
if (newX >= 0 && newX < board.getWidth()) {
|
|
Piece targetPiece = board.getPieceAt(newX, newY);
|
|
if (targetPiece != null && targetPiece.isWhite() != isWhite) {
|
|
validMoves.add(new Position(newX, newY));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// En passant captures
|
|
// Check if there's an opponent pawn adjacent to this pawn that can be captured via en passant
|
|
for (int dx = -1; dx <= 1; dx += 2) { // Check left and right
|
|
int adjacentX = x + dx;
|
|
if (adjacentX >= 0 && adjacentX < board.getWidth()) {
|
|
// Check if there's an opponent pawn at the same rank
|
|
Piece adjacentPiece = board.getPieceAt(adjacentX, y);
|
|
if (adjacentPiece != null &&
|
|
adjacentPiece.getType() == PieceType.Pawn &&
|
|
adjacentPiece.isWhite() != isWhite) {
|
|
|
|
// Check if this pawn can be captured via en passant
|
|
if (board.canCaptureEnPassant(adjacentX, y)) {
|
|
// The capture square is diagonally forward from the capturing pawn
|
|
int captureY = y + direction;
|
|
if (captureY >= 0 && captureY < board.getHeight()) {
|
|
validMoves.add(new Position(adjacentX, captureY));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addRookMoves(List<Position> validMoves, Piece piece, Board board) {
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
|
|
// Directions: horizontal and vertical
|
|
int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
|
|
|
|
for (int[] dir : directions) {
|
|
int dx = dir[0];
|
|
int dy = dir[1];
|
|
|
|
int newX = x + dx;
|
|
int newY = y + dy;
|
|
boolean continueInDirection = true;
|
|
|
|
while (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight() && continueInDirection) {
|
|
Piece targetPiece = board.getPieceAt(newX, newY);
|
|
|
|
if (targetPiece == null) {
|
|
// Empty square
|
|
validMoves.add(new Position(newX, newY));
|
|
} else {
|
|
// Occupied square
|
|
if (targetPiece.isWhite() != isWhite) {
|
|
// Capture opponent's piece
|
|
validMoves.add(new Position(newX, newY));
|
|
}
|
|
continueInDirection = false; // Cannot move beyond a piece
|
|
}
|
|
|
|
if (continueInDirection) {
|
|
newX += dx;
|
|
newY += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addKnightMoves(List<Position> validMoves, Piece piece, Board board) {
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
|
|
// All possible knight moves: L-shapes
|
|
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 (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight()) {
|
|
Piece targetPiece = board.getPieceAt(newX, newY);
|
|
|
|
if (targetPiece == null || targetPiece.isWhite() != isWhite) {
|
|
// Empty square or can capture opponent's piece
|
|
validMoves.add(new Position(newX, newY));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addBishopMoves(List<Position> validMoves, Piece piece, Board board) {
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
|
|
// Directions: diagonals
|
|
int[][] directions = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
|
|
|
|
for (int[] dir : directions) {
|
|
int dx = dir[0];
|
|
int dy = dir[1];
|
|
|
|
int newX = x + dx;
|
|
int newY = y + dy;
|
|
boolean continueInDirection = true;
|
|
|
|
while (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight() && continueInDirection) {
|
|
Piece targetPiece = board.getPieceAt(newX, newY);
|
|
|
|
if (targetPiece == null) {
|
|
// Empty square
|
|
validMoves.add(new Position(newX, newY));
|
|
} else {
|
|
// Occupied square
|
|
if (targetPiece.isWhite() != isWhite) {
|
|
// Capture opponent's piece
|
|
validMoves.add(new Position(newX, newY));
|
|
}
|
|
continueInDirection = false; // Cannot move beyond a piece
|
|
}
|
|
|
|
if (continueInDirection) {
|
|
newX += dx;
|
|
newY += dy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void addKingMoves(List<Position> validMoves, Piece piece, Board board) {
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
|
|
// All adjacent squares (normal king moves)
|
|
for (int dx = -1; dx <= 1; dx++) {
|
|
for (int dy = -1; dy <= 1; dy++) {
|
|
if (dx == 0 && dy == 0) continue; // Skip the current position
|
|
|
|
int newX = x + dx;
|
|
int newY = y + dy;
|
|
|
|
if (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight()) {
|
|
Piece targetPiece = board.getPieceAt(newX, newY);
|
|
|
|
if (targetPiece == null || targetPiece.isWhite() != piece.isWhite()) {
|
|
// Empty square or can capture opponent's piece
|
|
validMoves.add(new Position(newX, newY));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add castling moves
|
|
CastlingHandler handler = new CastlingHandler(board);
|
|
List<CastlingHandler.Position> castlingPositions = handler.getCastlingPositions(piece);
|
|
for (CastlingHandler.Position castlingPos : castlingPositions) {
|
|
validMoves.add(new Position(castlingPos.x, castlingPos.y));
|
|
}
|
|
}
|
|
|
|
// Helper method to check if a list contains a position
|
|
private boolean containsPosition(List<Position> positions, Position target) {
|
|
for (Position pos : positions) {
|
|
if (pos.x == target.x && pos.y == target.y) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
public class Position {
|
|
int x;
|
|
int y;
|
|
|
|
public Position(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
public boolean equals(Object obj) {
|
|
if (this == obj) return true;
|
|
if (obj == null || getClass() != obj.getClass()) return false;
|
|
Position position = (Position) obj;
|
|
return x == position.x && y == position.y;
|
|
}
|
|
}
|
|
} |