diff --git a/OOP_1A2_Project/bin/backend/Board.class b/OOP_1A2_Project/bin/backend/Board.class index afa6e69..bdc69a0 100644 Binary files a/OOP_1A2_Project/bin/backend/Board.class and b/OOP_1A2_Project/bin/backend/Board.class differ diff --git a/OOP_1A2_Project/bin/backend/Move.class b/OOP_1A2_Project/bin/backend/Move.class index 2d98995..ec997d7 100644 Binary files a/OOP_1A2_Project/bin/backend/Move.class and b/OOP_1A2_Project/bin/backend/Move.class differ diff --git a/OOP_1A2_Project/src/backend/Board.java b/OOP_1A2_Project/src/backend/Board.java index 000669f..328735f 100644 --- a/OOP_1A2_Project/src/backend/Board.java +++ b/OOP_1A2_Project/src/backend/Board.java @@ -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; + } } \ No newline at end of file diff --git a/OOP_1A2_Project/src/backend/Enpassant.java b/OOP_1A2_Project/src/backend/Enpassant.java index 0de279d..a4fce6c 100644 --- a/OOP_1A2_Project/src/backend/Enpassant.java +++ b/OOP_1A2_Project/src/backend/Enpassant.java @@ -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; + } + } +} \ No newline at end of file diff --git a/OOP_1A2_Project/src/backend/Move.java b/OOP_1A2_Project/src/backend/Move.java index 27a85cd..ada3231 100644 --- a/OOP_1A2_Project/src/backend/Move.java +++ b/OOP_1A2_Project/src/backend/Move.java @@ -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 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 validMoves, Piece piece, Board board) { diff --git a/OOP_1A2_Project/src/backend/Sound.java b/OOP_1A2_Project/src/backend/Sound.java index 641017d..a3d34ec 100644 --- a/OOP_1A2_Project/src/backend/Sound.java +++ b/OOP_1A2_Project/src/backend/Sound.java @@ -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(); }