en-passant

This commit is contained in:
romca 2025-05-23 20:41:52 +02:00
parent 0b0ea138a6
commit 5dd5ac50ee
6 changed files with 214 additions and 7 deletions

View File

@ -20,6 +20,11 @@ public class Board {
// Add Sound instance
private Sound sound;
// En passant tracking
private Integer enPassantX = null; // File where en passant capture is possible
private Integer enPassantY = null; // Rank where the pawn that can be captured is located
private int enPassantTurn = -1; // Turn number when en passant became possible
public Board(int colNum, int lineNum) {
this.width = colNum;
this.height = lineNum;
@ -113,6 +118,7 @@ public class Board {
public void cleanBoard() {
pieces.clear();
clearEnPassant();
}
public String toString() {
@ -228,7 +234,13 @@ public class Board {
fileRep[y] = line;
}
fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber;
// Include en passant information in the file representation
String enPassantInfo = "";
if (enPassantX != null && enPassantY != null) {
enPassantInfo = "," + enPassantX + "," + enPassantY + "," + enPassantTurn;
}
fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber + enPassantInfo;
return fileRep;
}
@ -282,9 +294,23 @@ public class Board {
} else {
this.turnNumber = 0;
}
// Parse en passant information
if (turnData.length >= 5) {
try {
this.enPassantX = Integer.parseInt(turnData[2].trim());
this.enPassantY = Integer.parseInt(turnData[3].trim());
this.enPassantTurn = Integer.parseInt(turnData[4].trim());
} catch (NumberFormatException e) {
clearEnPassant();
}
} else {
clearEnPassant();
}
} else {
this.turnNumber = 0;
this.isWhiteTurn = true;
clearEnPassant();
}
}
@ -317,6 +343,11 @@ public class Board {
this.highlightedPositions = new ArrayList<>();
this.highlightedPositions.addAll(board.highlightedPositions);
// Copy en passant state
this.enPassantX = board.enPassantX;
this.enPassantY = board.enPassantY;
this.enPassantTurn = board.enPassantTurn;
// Initialize board history for copy constructor
this.boardHistory = new ArrayList<>();
@ -372,12 +403,15 @@ public class Board {
// Remove it from history
boardHistory.remove(boardHistory.size() - 1);
// Use existing methods to restore state
this.width = previousBoard.width;
this.height = previousBoard.height;
this.turnNumber = previousBoard.turnNumber;
this.isWhiteTurn = previousBoard.isWhiteTurn;
this.enPassantX = previousBoard.enPassantX;
this.enPassantY = previousBoard.enPassantY;
this.enPassantTurn = previousBoard.enPassantTurn;
// Use existing getPieces() method to restore pieces
this.pieces.clear();
for (Piece p : previousBoard.getPieces()) {
@ -395,10 +429,58 @@ public class Board {
return !boardHistory.isEmpty();
}
// Add cleanup method to properly dispose of sound resources
public void cleanup() {
if (sound != null) {
sound.cleanup();
}
}
public void setEnPassant(int x, int y) {
this.enPassantX = x;
this.enPassantY = y;
this.enPassantTurn = this.turnNumber;
}
public void clearEnPassant() {
this.enPassantX = null;
this.enPassantY = null;
this.enPassantTurn = -1;
}
public boolean canCaptureEnPassant(int x, int y) {
// En passant is only valid for one turn after the double pawn move
if (enPassantX == null || enPassantY == null) {
return false;
}
// Check if this is the immediate next turn
if (turnNumber != enPassantTurn + 1) {
return false;
}
// Check if the position matches
return enPassantX == x && enPassantY == y;
}
public void handlePawnDoubleMove(int fromX, int fromY, int toX, int toY) {
Piece pawn = getPieceAt(toX, toY);
if (pawn != null && pawn.getType() == PieceType.Pawn) {
if (Math.abs(toY - fromY) == 2) {
setEnPassant(toX, toY);
} else {
clearEnPassant();
}
} else {
clearEnPassant();
}
}
public Enpassant.Position getEnPassantTarget() {
if (enPassantX != null && enPassantY != null && canCaptureEnPassant(enPassantX, enPassantY)) {
return new Enpassant.Position(enPassantX, enPassantY);
}
return null;
}
}

View File

@ -1,5 +1,72 @@
package backend;
public class Enpassant {
private Board board;
public Enpassant(Board board) {
this.board = board;
}
}
public boolean isEnPassantValid(int fromX, int fromY, int toX, int toY) {
Piece movingPiece = board.getPieceAt(fromX, fromY);
if (movingPiece == null || movingPiece.getType() != PieceType.Pawn) {
return false;
}
// Must be a diagonal move
if (Math.abs(toX - fromX) != 1 || Math.abs(toY - fromY) != 1) {
return false;
}
if (board.getPieceAt(toX, toY) != null) {
return false;
}
Piece targetPawn = board.getPieceAt(toX, fromY); // Same rank as moving pawn
if (targetPawn == null || targetPawn.getType() != PieceType.Pawn) {
return false;
}
// Target pawn must be opposite color
if (targetPawn.isWhite() == movingPiece.isWhite()) {
return false;
}
return board.canCaptureEnPassant(toX, fromY);
}
public void executeEnPassant(int fromX, int fromY, int toX, int toY) {
Piece movingPawn = board.getPieceAt(fromX, fromY);
board.removePiece(toX, fromY);
board.removePiece(fromX, fromY);
board.setPiece(movingPawn.isWhite(), movingPawn.getType(), toX, toY);
}
public Position getCapturedPawnPosition(int toX, int toY) {
Piece targetPawn = board.getPieceAt(toX, toY - (board.isTurnWhite() ? -1 : 1));
if (targetPawn != null && targetPawn.getType() == PieceType.Pawn) {
return new Position(toX, toY - (board.isTurnWhite() ? -1 : 1));
}
return null;
}
public static class Position {
public int x;
public 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;
}
}
}

View File

@ -17,6 +17,10 @@ public class Move {
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;
@ -30,6 +34,10 @@ public class 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() {
@ -43,6 +51,11 @@ public class Move {
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));
@ -52,6 +65,9 @@ public class Move {
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) {
@ -63,6 +79,14 @@ public class Move {
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();
}
@ -82,6 +106,10 @@ public class Move {
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);
@ -111,6 +139,10 @@ public class Move {
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);
@ -145,6 +177,10 @@ public class Move {
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);
@ -230,7 +266,7 @@ public class Move {
int y = piece.getY();
boolean isWhite = piece.isWhite();
int direction = isWhite ? -1 : 1; // White pawns move up, black pawns move down
int direction = isWhite ? -1 : 1; // White pawns move up (decreasing y), black pawns move down (increasing y)
// Forward movement
int newY = y + direction;
@ -249,7 +285,7 @@ public class Move {
}
}
// Diagonal captures
// Diagonal captures (regular captures)
for (int dx = -1; dx <= 1; dx += 2) {
int newX = x + dx;
if (newX >= 0 && newX < board.getWidth()) {
@ -260,6 +296,29 @@ public class Move {
}
}
}
// 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) {

View File

@ -29,7 +29,6 @@ public class Sound {
public void playMoveSound() {
if (moveSound != null) {
// Stop the clip if it's already playing
if (moveSound.isRunning()) {
moveSound.stop();
}