diff --git a/OOP_1A2_Project/bin/backend/Board.class b/OOP_1A2_Project/bin/backend/Board.class index 15d678c..3924584 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 87c299c..0dd9d80 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 85a77dd..9a7bde7 100644 --- a/OOP_1A2_Project/src/backend/Board.java +++ b/OOP_1A2_Project/src/backend/Board.java @@ -3,40 +3,19 @@ package backend; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; -public class Board { - private int width; +public class Board { + private int width; private int height; private int turnNumber; private boolean isWhiteTurn; private ArrayList pieces; - private Set highlightedPositions = new HashSet<>(); - private Position lastMovedPiecePosition = null; - private boolean lastMoveWasDoublePawnMove = false; - private static class Position { - int x; - int y; - - public Position(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - 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; - } - - @Override - public int hashCode() { - return 31 * x + y; - } - } - + private Set highlightedPositions = new HashSet<>(); + private Integer selectedX = null; + private Integer selectedY = null; + + public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; @@ -46,665 +25,341 @@ public class Board { this.highlightedPositions = new HashSet<>(); } + public int getWidth() { return width; } + public int getHeight() { return height; } + public int getTurnNumber() { return turnNumber; } + + public boolean isTurnWhite() { return isWhiteTurn; } - - public void setPiece(boolean isWhite, PieceType type, int x, int y) { - for (int i = 0; i < pieces.size(); i++) { - Piece p = pieces.get(i); - if (p.getX() == x && p.getY() == y) { - pieces.remove(i); - break; - } - } - Piece newPiece = new Piece( x, y, type,isWhite); - pieces.add(newPiece); - } - - public void populateBoard() { - //TODO - cleanBoard(); - - PieceType pawn = PieceType.Pawn; - PieceType rook = PieceType.Rook; - PieceType knight = PieceType.Knight; - PieceType bishop = PieceType.Bishop; - PieceType queen = PieceType.Queen; - PieceType king = PieceType.King; - - //all the pawns - for (int x=0; x<8; x++) { - pieces.add(new Piece(x, 1, pawn, false)); - pieces.add(new Piece(x, 6, pawn, true)); - } - - //black pieces - pieces.add(new Piece(0, 0, rook, false)); - pieces.add(new Piece(1, 0, knight, false)); - pieces.add(new Piece(2, 0, bishop, false)); - pieces.add(new Piece(3, 0, queen, false)); - pieces.add(new Piece(4, 0, king, false)); - pieces.add(new Piece(5, 0, bishop, false)); - pieces.add(new Piece(6, 0, knight, false)); - pieces.add(new Piece(7, 0, rook, false)); - - - //white pieces - pieces.add(new Piece(0, 7, rook, true)); - pieces.add(new Piece(1, 7, knight, true)); - pieces.add(new Piece(2, 7, bishop, true)); - pieces.add(new Piece(3, 7, queen, true)); - pieces.add(new Piece(4, 7, king, true)); - pieces.add(new Piece(5, 7, bishop, true)); - pieces.add(new Piece(6, 7, knight, true)); - pieces.add(new Piece(7, 7, rook, true)); - } - - public void cleanBoard() { - pieces.clear(); - } - - public String toString() { - String result = ""; - for (int y = 0; y < height; y++) { - for (int x = 0; x < width; x++) { - String c = " "; - for (Piece p : pieces) { - if (p.getX() == x && p.getY() == y) { - String letter = p.getType().getSummary(); - if (p.isWhite()) { - c = "W" + letter; - } else { - c = "B" + letter; - } - } - } - result += c + ","; - } - result += "\n"; - } - return result; - } - - public ArrayList getPieces() { - ArrayList result = new ArrayList<>(); - for (Piece p : this.pieces) { - result.add(p); - } - return result; - } - public Piece getPieceAt(int x, int y) { - for (Piece p : pieces) { - if (p.getX() == x && p.getY() == y) { - return p; - } - } - return null; - } - - private Integer selectedX = null; - private Integer selectedY = null; - - public void userTouch(int x, int y) { - Piece clickedPiece = getPieceAt(x, y); - - if (selectedX == null || selectedY == null) { - // No piece selected yet - if (clickedPiece != null && clickedPiece.isWhite() == isWhiteTurn) { - selectedX = x; - selectedY = y; - calculateHighlights(); // Calculate valid moves - } - } else { - if (selectedX == x && selectedY == y) { - // Unselection - selectedX = null; - selectedY = null; - clearHighlights(); // Clear highlights - } else { - Piece selectedPiece = getPieceAt(selectedX, selectedY); - if (selectedPiece != null) { - // Check if the destination is a valid move - if (isHighlighted(x, y)) { - - // Check if this is an en passant capture - boolean isEnPassant = isEnPassantCapture(selectedPiece, x, y); - - // Remove captured piece (if any) - Piece toRemove = getPieceAt(x, y); - if (toRemove != null) { - pieces.remove(toRemove); - } - - // Handle en passant capture (remove the captured pawn) - if (isEnPassant) { - int capturedPawnY = selectedPiece.isWhite() ? y + 1 : y - 1; - Piece capturedPawn = getPieceAt(x, capturedPawnY); - if (capturedPawn != null) { - pieces.remove(capturedPawn); - } - } - - // Track if this move is a double pawn move - boolean isDoublePawnMove = selectedPiece.getType() == PieceType.Pawn && - Math.abs(y - selectedPiece.getY()) == 2; - - // Move piece - selectedPiece.moveTo(x, y); - - // Update en passant tracking - if (isDoublePawnMove) { - lastMovedPiecePosition = new Position(x, y); - lastMoveWasDoublePawnMove = true; - } else { - lastMovedPiecePosition = null; - lastMoveWasDoublePawnMove = false; - } - - // Advance turn - turnNumber++; - isWhiteTurn = !isWhiteTurn; - - // Unselect and clear highlights - selectedX = null; - selectedY = null; - clearHighlights(); - } else if (clickedPiece != null && clickedPiece.isWhite() == isWhiteTurn) { - // Select a different piece of same color - selectedX = x; - selectedY = y; - calculateHighlights(); // Calculate valid moves for new piece - } - } - } - } - } - - // Add this method to check if a move is an en passant capture: - private boolean isEnPassantCapture(Piece piece, int targetX, int targetY) { - if (piece.getType() != PieceType.Pawn) return false; - if (!lastMoveWasDoublePawnMove) return false; - if (lastMovedPiecePosition == null) return false; - - // Check if we're capturing diagonally to an empty square - if (getPieceAt(targetX, targetY) != null) return false; - - // Check if the target is diagonally adjacent - if (Math.abs(targetX - piece.getX()) != 1) return false; - if (Math.abs(targetY - piece.getY()) != 1) return false; - - // Check if there's an opponent pawn next to us that just moved two squares - int adjacentPawnY = piece.getY(); - Piece adjacentPawn = getPieceAt(targetX, adjacentPawnY); - - if (adjacentPawn == null) return false; - if (adjacentPawn.getType() != PieceType.Pawn) return false; - if (adjacentPawn.isWhite() == piece.isWhite()) return false; - - // Check if this adjacent pawn is the one that just moved two squares - return lastMovedPiecePosition.x == targetX && lastMovedPiecePosition.y == adjacentPawnY; - } - - public boolean isSelected(int x, int y) { - // Check if the selected coordinates match the given x and y - if (selectedX != null && selectedY != null) { - return selectedX == x && selectedY == y; - } - return false; - } - /* saving-loading feature :*/ - - public String[] toFileRep() { - String[] fileRep = new String[height + 2]; - - String boardStr = toString(); - - String[] boardLines = boardStr.split("\n"); - - for (int y = 0; y < height && y < boardLines.length; y++) { - String line = boardLines[y]; - if (line.endsWith(",")) { - line = line.substring(0, line.length() - 1); - } - fileRep[y] = line; - } - - fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber; - - // Save en passant state - String enPassantInfo = lastMoveWasDoublePawnMove ? "1" : "0"; - if (lastMovedPiecePosition != null) { - enPassantInfo += "," + lastMovedPiecePosition.x + "," + lastMovedPiecePosition.y; - } - fileRep[height + 1] = enPassantInfo; - - return fileRep; - } - - public Board(String[] array) { - this.width = 8; - this.height = 8; - this.pieces = new ArrayList<>(); - - if (array == null || array.length < 9) { - this.turnNumber = 0; - this.isWhiteTurn = true; - this.lastMoveWasDoublePawnMove = false; - this.lastMovedPiecePosition = null; - populateBoard(); - return; - } - - for (int y = 0; y < height; y++) { - if (y >= array.length) break; - - String line = array[y]; - String[] squares = line.split(","); - - for (int x = 0; x < width && x < squares.length; x++) { - String square = squares[x].trim(); - - if (square.length() < 2 || square.equals(" ")) continue; - - boolean isWhite = square.charAt(0) == 'W'; - char pieceChar = square.charAt(1); - - PieceType type = PieceType.fromSummary(pieceChar); - - pieces.add(new Piece(x, y, type, isWhite)); - } - } - - if (array.length > height) { - String turnInfo = array[height]; - String[] turnData = turnInfo.split(","); - - if (turnData.length > 0) { - this.isWhiteTurn = turnData[0].trim().equals("W"); - } else { - this.isWhiteTurn = true; - } - - if (turnData.length > 1) { - try { - this.turnNumber = Integer.parseInt(turnData[1].trim()); - } catch (NumberFormatException e) { - this.turnNumber = 0; - } - } else { - this.turnNumber = 0; - } - } else { - this.turnNumber = 0; - this.isWhiteTurn = true; - } - - // Load en passant state - if (array.length > height + 1) { - String enPassantInfo = array[height + 1]; - String[] enPassantData = enPassantInfo.split(","); - - if (enPassantData.length > 0) { - this.lastMoveWasDoublePawnMove = enPassantData[0].trim().equals("1"); - } else { - this.lastMoveWasDoublePawnMove = false; - } - - if (enPassantData.length >= 3) { - try { - int x = Integer.parseInt(enPassantData[1].trim()); - int y = Integer.parseInt(enPassantData[2].trim()); - this.lastMovedPiecePosition = new Position(x, y); - } catch (NumberFormatException e) { - this.lastMovedPiecePosition = null; - } - } else { - this.lastMovedPiecePosition = null; - } - } else { - this.lastMoveWasDoublePawnMove = false; - this.lastMovedPiecePosition = null; - } - - } - public boolean isHighlighted(int x, int y) { - return highlightedPositions.contains(new Position(x, y)); - } - - public void undoLastMove() { - //TODO - - } - - public Board(Board board) { - this.width = board.width; - this.height = board.height; - this.turnNumber = board.turnNumber; - this.isWhiteTurn = board.isWhiteTurn; - - // Deep copy pieces - this.pieces = new ArrayList<>(); - for (Piece p : board.pieces) { - this.pieces.add(new Piece(p.getX(), p.getY(), p.getType(), p.isWhite())); - } - - // Copy selection - this.selectedX = board.selectedX; - this.selectedY = board.selectedY; - - // Copy highlighted positions - this.highlightedPositions = new HashSet<>(); - this.highlightedPositions.addAll(board.highlightedPositions); - - // Copy en passant state - this.lastMovedPiecePosition = board.lastMovedPiecePosition; - this.lastMoveWasDoublePawnMove = board.lastMoveWasDoublePawnMove; - } - - // Add these methods after userTouch or before isHighlighted - private void clearHighlights() { - highlightedPositions.clear(); - } - - private void calculateHighlights() { - if (selectedX == null || selectedY == null) { - return; - } - - Piece selectedPiece = getPieceAt(selectedX, selectedY); - if (selectedPiece == null) { - return; - } - - // Get valid moves for the selected piece - Set validMoves = getValidMoves(selectedPiece); - - // Update highlighted positions - highlightedPositions.clear(); - highlightedPositions.addAll(validMoves); - } - private Set getValidMoves(Piece piece) { - // Get all possible moves without considering check - Set candidateMoves = getRawValidMoves(piece); - - // Filter out moves that would leave king in check - Set legalMoves = new HashSet<>(); - for (Position move : candidateMoves) { - if (!wouldMoveLeaveKingInCheck(piece, move.x, move.y)) { - legalMoves.add(move); - } - } - - return legalMoves; - } - - // Add valid pawn moves - private void addPawnMoves(Set validMoves, int x, int y, boolean isWhite) { - int direction = isWhite ? -1 : 1; // White pawns move up, black pawns move down - - // Forward movement - int newY = y + direction; - if (newY >= 0 && newY < height) { - // Single square forward - if (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 < height && getPieceAt(x, doubleY) == null) { - validMoves.add(new Position(x, doubleY)); - } - } - } - - // Diagonal captures - for (int dx = -1; dx <= 1; dx += 2) { - int newX = x + dx; - if (newX >= 0 && newX < width) { - Piece targetPiece = getPieceAt(newX, newY); - if (targetPiece != null && targetPiece.isWhite() != isWhite) { - validMoves.add(new Position(newX, newY)); - } - // En passant capture - else if (targetPiece == null && canCaptureEnPassant(x, y, newX, newY, isWhite)) { - validMoves.add(new Position(newX, newY)); - } - } - } - } - } - - // Add this helper method for en passant validation: - private boolean canCaptureEnPassant(int fromX, int fromY, int toX, int toY, boolean isWhite) { - if (!lastMoveWasDoublePawnMove) return false; - if (lastMovedPiecePosition == null) return false; - - // Check if there's an opponent pawn adjacent to us - Piece adjacentPawn = getPieceAt(toX, fromY); - if (adjacentPawn == null) return false; - if (adjacentPawn.getType() != PieceType.Pawn) return false; - if (adjacentPawn.isWhite() == isWhite) return false; - - // Check if this is the pawn that just moved two squares - return lastMovedPiecePosition.x == toX && lastMovedPiecePosition.y == fromY; - } - - // Add valid rook moves - private void addRookMoves(Set validMoves, int x, int y, boolean 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; - - while (newX >= 0 && newX < width && newY >= 0 && newY < height) { - Piece targetPiece = 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)); - } - break; // Cannot move beyond a piece - } - - newX += dx; - newY += dy; - } - } - } - - // Add valid knight moves - private void addKnightMoves(Set validMoves, int x, int y, boolean 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 < width && newY >= 0 && newY < height) { - Piece targetPiece = getPieceAt(newX, newY); - - if (targetPiece == null || targetPiece.isWhite() != isWhite) { - // Empty square or can capture opponent's piece - validMoves.add(new Position(newX, newY)); - } - } - } - } - - // Add valid bishop moves - private void addBishopMoves(Set validMoves, int x, int y, boolean 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; - - while (newX >= 0 && newX < width && newY >= 0 && newY < height) { - Piece targetPiece = 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)); - } - break; // Cannot move beyond a piece - } - - newX += dx; - newY += dy; - } - } - } - - // Add valid king moves - private void addKingMoves(Set validMoves, int x, int y, boolean isWhite) { - // All adjacent squares - 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 < width && newY >= 0 && newY < height) { - Piece targetPiece = getPieceAt(newX, newY); - - if (targetPiece == null || targetPiece.isWhite() != isWhite) { - // Empty square or can capture opponent's piece - validMoves.add(new Position(newX, newY)); - } - } - } - } - } - // Add this method to check if a king is in check - private boolean isKingInCheck(boolean isWhiteKing) { - // Find the king's position - Piece king = null; - for (Piece p : pieces) { - if (p.getType() == PieceType.King && p.isWhite() == isWhiteKing) { - king = p; - break; - } - } - - if (king == null) return false; - - // Check if any opponent piece can attack the king - for (Piece p : pieces) { - if (p.isWhite() == isWhiteKing) continue; // Skip pieces of same color - - // Get raw moves without check validation - Set attackMoves = getRawValidMoves(p); - - // If any piece can move to king's position, king is in check - if (attackMoves.contains(new Position(king.getX(), king.getY()))) { - return true; - } - } - - return false; - } - - // Add this method to simulate a move and check if it leaves king in check - private boolean wouldMoveLeaveKingInCheck(Piece piece, int newX, int newY) { - // Create temporary board to simulate move - Board tempBoard = new Board(this); - - // Find the piece in the temp board - Piece tempPiece = null; - for (Piece p : tempBoard.pieces) { - if (p.getX() == piece.getX() && p.getY() == piece.getY()) { - tempPiece = p; - break; - } - } - - if (tempPiece == null) return true; // Safety check - - // Remove any piece at destination - Piece capturedPiece = tempBoard.getPieceAt(newX, newY); - if (capturedPiece != null) { - tempBoard.pieces.remove(capturedPiece); - } - - // Move the piece - tempPiece.moveTo(newX, newY); - - // Check if king is in check after move - return tempBoard.isKingInCheck(piece.isWhite()); - } - - // Add this helper method to get moves without check validation - private Set getRawValidMoves(Piece piece) { - Set moves = new HashSet<>(); - - switch (piece.getType()) { - case Pawn: - addPawnMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - case Rook: - addRookMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - case Knight: - addKnightMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - case Bishop: - addBishopMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - case Queen: - addRookMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - addBishopMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - case King: - addKingMoves(moves, piece.getX(), piece.getY(), piece.isWhite()); - break; - } - - return moves; - } - public void playMove(Move move) { - //TODO - - } - -} + + public void advanceTurn() { + turnNumber++; + isWhiteTurn = !isWhiteTurn; + } + + + public void setPiece(boolean isWhite, PieceType type, int x, int y) { + for (int i = 0; i < pieces.size(); i++) { + Piece p = pieces.get(i); + if (p.getX() == x && p.getY() == y) { + pieces.remove(i); + break; + } + } + Piece newPiece = new Piece(x, y, type, isWhite); + pieces.add(newPiece); + } + + + public void removePiece(int x, int y) { + for (int i = 0; i < pieces.size(); i++) { + Piece p = pieces.get(i); + if (p.getX() == x && p.getY() == y) { + pieces.remove(i); + break; + } + } + } + + + public void populateBoard() { + cleanBoard(); + + PieceType pawn = PieceType.Pawn; + PieceType rook = PieceType.Rook; + PieceType knight = PieceType.Knight; + PieceType bishop = PieceType.Bishop; + PieceType queen = PieceType.Queen; + PieceType king = PieceType.King; + + // All the pawns + for (int x = 0; x < 8; x++) { + pieces.add(new Piece(x, 1, pawn, false)); + pieces.add(new Piece(x, 6, pawn, true)); + } + + // Black pieces + pieces.add(new Piece(0, 0, rook, false)); + pieces.add(new Piece(1, 0, knight, false)); + pieces.add(new Piece(2, 0, bishop, false)); + pieces.add(new Piece(3, 0, queen, false)); + pieces.add(new Piece(4, 0, king, false)); + pieces.add(new Piece(5, 0, bishop, false)); + pieces.add(new Piece(6, 0, knight, false)); + pieces.add(new Piece(7, 0, rook, false)); + + // White pieces + pieces.add(new Piece(0, 7, rook, true)); + pieces.add(new Piece(1, 7, knight, true)); + pieces.add(new Piece(2, 7, bishop, true)); + pieces.add(new Piece(3, 7, queen, true)); + pieces.add(new Piece(4, 7, king, true)); + pieces.add(new Piece(5, 7, bishop, true)); + pieces.add(new Piece(6, 7, knight, true)); + pieces.add(new Piece(7, 7, rook, true)); + } + + + public void cleanBoard() { + pieces.clear(); + } + + + public String toString() { + String result = ""; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + String c = " "; + for (Piece p : pieces) { + if (p.getX() == x && p.getY() == y) { + String letter = p.getType().getSummary(); + if (p.isWhite()) { + c = "W" + letter; + } else { + c = "B" + letter; + } + } + } + result += c + ","; + } + result += "\n"; + } + return result; + } + + + public ArrayList getPieces() { + ArrayList result = new ArrayList<>(); + for (Piece p : this.pieces) { + result.add(p); + } + return result; + } + + + public Piece getPieceAt(int x, int y) { + for (Piece p : pieces) { + if (p.getX() == x && p.getY() == y) { + return p; + } + } + return null; + } + + + public void userTouch(int x, int y) { + Piece clickedPiece = getPieceAt(x, y); + + if (selectedX == null || selectedY == null) { + // No piece selected yet + if (clickedPiece != null && clickedPiece.isWhite() == isWhiteTurn) { + selectedX = x; + selectedY = y; + calculateHighlights(); // Calculate valid moves + } + } else { + if (selectedX == x && selectedY == y) { + // Unselection + selectedX = null; + selectedY = null; + clearHighlights(); // Clear highlights + } else { + Piece selectedPiece = getPieceAt(selectedX, selectedY); + if (selectedPiece != null) { + // Check if the destination is a valid move + if (isHighlighted(x, y)) { + // Create and execute the move + Move move = new Move(selectedX, selectedY, x, y, this); + if (move.isValid()) { + move.execute(); + + // Check for checkmate + if (move.putsOpponentInCheckmate()) { + System.out.println("Checkmate! " + (isWhiteTurn ? "Black" : "White") + " wins!"); + } else if (move.putsOpponentInCheck()) { + System.out.println("Check!"); + } + } + + // Unselect and clear highlights + selectedX = null; + selectedY = null; + clearHighlights(); + } else if (clickedPiece != null && clickedPiece.isWhite() == isWhiteTurn) { + // Select a different piece of same color + selectedX = x; + selectedY = y; + calculateHighlights(); // Calculate valid moves for new piece + } + } + } + } + } + + + public boolean isSelected(int x, int y) { + // Check if the selected coordinates match the given x and y + if (selectedX != null && selectedY != null) { + return selectedX == x && selectedY == y; + } + return false; + } + + + public String[] toFileRep() { + String[] fileRep = new String[height + 1]; + + String boardStr = toString(); + + String[] boardLines = boardStr.split("\n"); + + for (int y = 0; y < height && y < boardLines.length; y++) { + String line = boardLines[y]; + if (line.endsWith(",")) { + line = line.substring(0, line.length() - 1); + } + fileRep[y] = line; + } + + fileRep[height] = (isWhiteTurn ? "W" : "B") + "," + turnNumber; + + return fileRep; + } + + + public Board(String[] array) { + this.width = 8; + this.height = 8; + this.pieces = new ArrayList<>(); + + if (array == null || array.length < 9) { + this.turnNumber = 0; + this.isWhiteTurn = true; + populateBoard(); + return; + } + + for (int y = 0; y < height; y++) { + if (y >= array.length) break; + + String line = array[y]; + String[] squares = line.split(","); + + for (int x = 0; x < width && x < squares.length; x++) { + String square = squares[x].trim(); + + if (square.length() < 2 || square.equals(" ")) continue; + + boolean isWhite = square.charAt(0) == 'W'; + char pieceChar = square.charAt(1); + + PieceType type = PieceType.fromSummary(pieceChar); + + pieces.add(new Piece(x, y, type, isWhite)); + } + } + + if (array.length > height) { + String turnInfo = array[height]; + String[] turnData = turnInfo.split(","); + + if (turnData.length > 0) { + this.isWhiteTurn = turnData[0].trim().equals("W"); + } else { + this.isWhiteTurn = true; + } + + if (turnData.length > 1) { + try { + this.turnNumber = Integer.parseInt(turnData[1].trim()); + } catch (NumberFormatException e) { + this.turnNumber = 0; + } + } else { + this.turnNumber = 0; + } + } else { + this.turnNumber = 0; + this.isWhiteTurn = true; + } + } + + + public boolean isHighlighted(int x, int y) { + return highlightedPositions.contains(new Move.Position(x, y)); + } + + + public void undoLastMove() { + // TODO + } + + + public Board(Board board) { + this.width = board.width; + this.height = board.height; + this.turnNumber = board.turnNumber; + this.isWhiteTurn = board.isWhiteTurn; + + // Deep copy pieces + this.pieces = new ArrayList<>(); + for (Piece p : board.pieces) { + this.pieces.add(new Piece(p.getX(), p.getY(), p.getType(), p.isWhite())); + } + + // Copy selection + this.selectedX = board.selectedX; + this.selectedY = board.selectedY; + + // Copy highlighted positions + this.highlightedPositions = new HashSet<>(); + this.highlightedPositions.addAll(board.highlightedPositions); + } + + + private void clearHighlights() { + highlightedPositions.clear(); + } + + + private void calculateHighlights() { + if (selectedX == null || selectedY == null) { + return; + } + + Piece selectedPiece = getPieceAt(selectedX, selectedY); + if (selectedPiece == null) { + return; + } + + // Get valid moves for the selected piece using the Move class + Set validMoves = Move.getValidMoves(selectedPiece, this); + + // Update highlighted positions + highlightedPositions.clear(); + highlightedPositions.addAll(validMoves); + } + + + public void playMove(Move move) { + if (move.isValid() && !move.putsOwnKingInCheck()) { + move.execute(); + } + } +} \ 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 5f64282..faf1019 100644 --- a/OOP_1A2_Project/src/backend/Move.java +++ b/OOP_1A2_Project/src/backend/Move.java @@ -1,5 +1,384 @@ package backend; +import java.util.HashSet; +import java.util.Set; + public class Move { - -} + private int fromX; + private int fromY; + private int toX; + private int toY; + private Piece movingPiece; + private Piece capturedPiece; + private Board board; + + 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); + } + + public boolean isValid() { + // Check if the moving piece exists + if (movingPiece == null) { + return false; + } + + // Check if the move is in the set of valid moves for this piece + Set validMoves = getValidDestinations(movingPiece, board); + return validMoves.contains(new Position(toX, toY)); + } + + public void execute() { + // 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); + + // 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 + + // 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 + return isKingInCheck(tempBoard, 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 + + // 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 + return isKingInCheck(tempBoard, !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 + + // 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 + + Set 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 static Set getValidDestinations(Piece piece, Board board) { + Set moves = new HashSet<>(); + + 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 static Set getValidMoves(Piece piece, Board board) { + // Get all possible moves without considering check + Set candidateMoves = getValidDestinations(piece, board); + + // Filter out moves that would leave king in check + Set legalMoves = new HashSet<>(); + 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 static boolean isKingInCheck(Board board, boolean isWhiteKing) { + // Find the king's position + Piece king = null; + for (Piece p : board.getPieces()) { + if (p.getType() == PieceType.King && p.isWhite() == isWhiteKing) { + king = p; + break; + } + } + + if (king == null) return false; + + // Check if any opponent piece can attack the king + for (Piece p : board.getPieces()) { + if (p.isWhite() == isWhiteKing) continue; // Skip pieces of same color + + // Get raw moves without check validation + Set attackMoves = getValidDestinations(p, board); + + // If any piece can move to king's position, king is in check + if (attackMoves.contains(new Position(king.getX(), king.getY()))) { + return true; + } + } + + return false; + } + + private static void addPawnMoves(Set 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, black pawns move down + + // 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 + 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)); + } + } + } + } + } + + private static void addRookMoves(Set 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; + + while (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight()) { + 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)); + } + break; // Cannot move beyond a piece + } + + newX += dx; + newY += dy; + } + } + } + + private static void addKnightMoves(Set 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 static void addBishopMoves(Set 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; + + while (newX >= 0 && newX < board.getWidth() && newY >= 0 && newY < board.getHeight()) { + 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)); + } + break; // Cannot move beyond a piece + } + + newX += dx; + newY += dy; + } + } + } + + private static void addKingMoves(Set validMoves, Piece piece, Board board) { + int x = piece.getX(); + int y = piece.getY(); + boolean isWhite = piece.isWhite(); + + // All adjacent squares + 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() != isWhite) { + // Empty square or can capture opponent's piece + validMoves.add(new Position(newX, newY)); + } + } + } + } + } + + public static class Position { + int x; + int y; + + public Position(int x, int y) { + this.x = x; + this.y = y; + } + + @Override + 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; + } + + @Override + public int hashCode() { + return 31 * x + y; + } + } +} \ No newline at end of file