diff --git a/src/main/java/chessmaster/commands/ShowMovesCommand.java b/src/main/java/chessmaster/commands/ShowMovesCommand.java index a4ff492f24..92f8a93421 100644 --- a/src/main/java/chessmaster/commands/ShowMovesCommand.java +++ b/src/main/java/chessmaster/commands/ShowMovesCommand.java @@ -40,7 +40,7 @@ public CommandResult execute(Game game) throws ChessMasterException { throw new NullPieceException(coord); } - Coordinate[] possibleCoordinates = piece.getFlattenedFilteredCoordinates(board); + Coordinate[] possibleCoordinates = piece.getLegalCoordinates(board); ui.printChessBoardAvailableMoves(board.getBoard(), piece, possibleCoordinates); String[] displayString = piece.getAvailableCoordinatesString(board); diff --git a/src/main/java/chessmaster/game/ChessBoard.java b/src/main/java/chessmaster/game/ChessBoard.java index 257eb7b38e..57a2ea458e 100644 --- a/src/main/java/chessmaster/game/ChessBoard.java +++ b/src/main/java/chessmaster/game/ChessBoard.java @@ -102,17 +102,7 @@ public int getDifficulty() { return this.difficulty; } - public boolean isChecked(Color color) { - Move[] moves = getAllMoves(color.getOppositeColour()); - for (Move move : moves) { - Coordinate to = move.getTo(); - if (this.getPieceAtCoor(to) instanceof King) { - return true; - } - } - return false; - } - + //@@author onx001 public boolean hasEnPassant() { //Checks all chess pieces for en passant for (int row = 0; row < ChessBoard.SIZE; row++) { @@ -163,8 +153,40 @@ public boolean isCheckmated(Color color) { return moves.length == 0; } + /** + * Check if the player of the specified color is in check. + * + * This method determines whether the player with the specified color is in check, + * meaning their king is under threat. It checks if any moves by the opposing player's pieces + * can reach the player's king. + * + * @param color The color for which to check if the king is in check ('WHITE' or 'BLACK'). + * @return `true` if the player is in check; `false` if the player's king is not in immediate danger. + */ + public boolean isChecked(Color color) { + Move[] moves = getPseudoLegalMoves(color.getOppositeColour()); + for (Move move : moves) { + Coordinate to = move.getTo(); + if (this.getPieceAtCoor(to) instanceof King) { + return true; + } + } + return false; + } + + /** + * Retrieve an array of pseudo-legal moves for pieces of the specified color. + * + * This method calculates and provides an array of pseudo-legal moves for all pieces of the given + * color on the chessboard. Pseudo-legal moves are those that are valid based on the piece's movement rules, + * but they may not account for potential checks on the king. + * + * @param color The color for which pseudo-legal moves should be generated ('WHITE' or 'BLACK'). + * @return An array of Move objects, each representing a pseudo-legal move, containing the starting square, + * destination square, and the ChessPiece involved. + */ - public Move[] getAllMoves(Color color) { + public Move[] getPseudoLegalMoves(Color color) { //Declare arraylist of moves as allMoves ArrayList allMoves = new ArrayList<>(); @@ -174,7 +196,7 @@ public Move[] getAllMoves(Color color) { ChessPiece piece = getPieceAtCoor(currentCoor); if (piece.isSameColorAs(color)) { - Coordinate[] possibleCoordinates = piece.getLegalCoordinates(this); + Coordinate[] possibleCoordinates = piece.getPseudoLegalCoordinates(this); for (Coordinate possible: possibleCoordinates) { allMoves.add(MoveFactory.createMove(this, currentCoor, possible)); } @@ -184,9 +206,24 @@ public Move[] getAllMoves(Color color) { return allMoves.toArray(new Move[0]); } + /** + * Retrieve an array of legal moves for pieces of the specified color. + * + * This method calculates and provides an array of legal moves for all pieces of the given color on the + * chessboard. This is done by executing each pseudo-legal move and ensuring that it does not result in + * the king being checked. + * + * Legal moves are those that adhere to the piece's movement rules and do not result in the + * player's own king being in check. + * + * @param color The color for which legal moves should be generated ('WHITE' or 'BLACK'). + * @return An array of Move objects, each representing a legal move, containing the starting square, + * destination square, and the ChessPiece involved. + */ public Move[] getLegalMoves(Color color) { - Move[] moves = getAllMoves(color); - ArrayList uncheckedMoves = new ArrayList<>(); + Move[] moves = getPseudoLegalMoves(color); + ArrayList legalMoves = new ArrayList<>(); + for (Move move : moves) { ChessBoard newBoard = this.clone(); Coordinate from = move.getFrom(); @@ -198,10 +235,10 @@ public Move[] getLegalMoves(Color color) { continue; } if (!newBoard.isChecked(color)) { - uncheckedMoves.add(move); + legalMoves.add(move); } } - return uncheckedMoves.toArray(new Move[0]); + return legalMoves.toArray(new Move[0]); } //@@author TongZhengHong diff --git a/src/main/java/chessmaster/game/Move.java b/src/main/java/chessmaster/game/Move.java index 91bb47b325..d6b0b1d600 100644 --- a/src/main/java/chessmaster/game/Move.java +++ b/src/main/java/chessmaster/game/Move.java @@ -73,12 +73,10 @@ public void setPieceMoved(ChessPiece pieceMoved) { * @return */ public boolean isValid(ChessBoard board) { - Coordinate[][] coordinates = pieceMoved.getPseudoCoordinates(board); - for (Coordinate[] direction : coordinates) { - for (Coordinate coor : direction) { - if (coor.equals(to)) { - return true; - } + Coordinate[] coordinates = pieceMoved.getPseudoLegalCoordinates(board); + for (Coordinate coor : coordinates) { + if (coor.equals(to)) { + return true; } } return false; @@ -148,7 +146,10 @@ public String toFileString() { public boolean equals(Object obj) { if (obj != null && obj instanceof Move) { final Move other = (Move) obj; - return from.equals(other.getFrom()) && to.equals(other.getTo()) && pieceMoved.equals(other.getPieceMoved()); + boolean sameFrom = from.equals(other.getFrom()); + boolean sameTo = to.equals(other.getTo()); + boolean samePiece = pieceMoved.getPieceName().equals(other.getPieceMoved().getPieceName()); + return sameFrom && sameTo && samePiece; } return false; } diff --git a/src/main/java/chessmaster/pieces/Bishop.java b/src/main/java/chessmaster/pieces/Bishop.java index ad9cf55a4d..3b74ee9e52 100644 --- a/src/main/java/chessmaster/pieces/Bishop.java +++ b/src/main/java/chessmaster/pieces/Bishop.java @@ -39,7 +39,7 @@ public Bishop(int row, int col, Color color) { * is of the coordinates in that direction. */ @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS.length][0]; for (int dir = 0; dir < DIRECTIONS.length; dir++) { @@ -68,7 +68,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { result[dir] = possibleCoordInDirection.toArray(new Coordinate[0]); } - return result; + return flattenArray(result); } @Override diff --git a/src/main/java/chessmaster/pieces/ChessPiece.java b/src/main/java/chessmaster/pieces/ChessPiece.java index 2d1835ef49..68fb7d0743 100644 --- a/src/main/java/chessmaster/pieces/ChessPiece.java +++ b/src/main/java/chessmaster/pieces/ChessPiece.java @@ -6,7 +6,6 @@ import chessmaster.game.Move; import java.util.ArrayList; -import java.util.Arrays; public abstract class ChessPiece { @@ -68,62 +67,65 @@ public boolean isSameAs(ChessPiece other) { return (this.toString().equals(other.toString())); } /** - * Returns available coordinates in multiple directions from the current position. - * The directions are dependent on the chess piece type. Each inner array stores the coordinates that is - * in the direction the current chess piece can move to. + * Gets an array of pseudo-legal coordinates for potential moves. * - * @return A 2D array of Coordinate arrays representing available coordinates in different directions. + * This abstract method is meant to be implemented by subclasses to provide an array of pseudo-legal + * coordinates representing potential moves for a specific chess piece. The coordinates are based on + * the piece's movement rules and the current state of the chessboard. + * + * @param board The current state of the chessboard. + * @return An array of Coordinate objects, each representing a pseudo-legal move or destination for the chess piece. */ - public abstract Coordinate[][] getPseudoCoordinates(ChessBoard board); + public abstract Coordinate[] getPseudoLegalCoordinates(ChessBoard board); - //@@author onx001 /** - * Get a flattened array of valid coordinates for the chess piece's moves based on its available coordinates - * and the current state of the ChessBoard. + * Flatten a 2D array of coordinates into a 1D array. + * + * This method takes a 2D array of coordinates, representing various directions, + * and flattens it into a 1D array for easy access and processing. * - * @param board The ChessBoard representing the current game state. - * @return A 1D array of valid coordinates for the piece's legal moves. + * @param coordInDirections A 2D array of coordinates to be flattened. + * @return A 1D array of coordinates, combining all coordinates from the input directions. */ - public Coordinate[] getLegalCoordinates(ChessBoard board) { - Coordinate[][] availableCoordinates = getPseudoCoordinates(board); + public Coordinate[] flattenArray(Coordinate[][] coordInDirections) { ArrayList flattenedCoordinates = new ArrayList<>(); - - for (Coordinate[] direction : availableCoordinates) { - for (Coordinate possibleCoord : direction) { - flattenedCoordinates.add(possibleCoord); + for (Coordinate[] direction : coordInDirections) { + for (Coordinate coordinate : direction) { + flattenedCoordinates.add(coordinate); } } return flattenedCoordinates.toArray(new Coordinate[0]); } - public Coordinate[][] getFilteredCoordinates(ChessBoard board) { - Move[] allMoves = board.getLegalMoves(this.color); - ArrayList filteredCoordinates = new ArrayList<>(); - - for (Coordinate[] direction : getPseudoCoordinates(board)) { - ArrayList filteredDirection = new ArrayList<>(); - for (Coordinate possibleCoord : direction) { - for (Move move : allMoves) { - if (move.getFrom().equals(this.position) && move.getTo().equals(possibleCoord)) { - filteredDirection.add(possibleCoord); - } + //@@author onx001 + /** + * Gets an array of legal coordinates representing potential moves that adhere to game rules. + * + * This method calculates and provides an array of legal coordinates, which represent potential + * moves for the chess piece, based on its movement rules and the current state of the chessboard. + * These coordinates are filtered to ensure they adhere to the game rules, including preventing + * moves that result in the player's own king being in check. + * + * @param board The current state of the chessboard. + * @return An array of Coordinate objects, each representing a legal move or destination for the chess piece. + */ + public Coordinate[] getLegalCoordinates(ChessBoard board) { + Coordinate[] pseudoLegalCoordinates = getPseudoLegalCoordinates(board); + Move[] allLegalMoves = board.getLegalMoves(this.color); + + ArrayList result = new ArrayList<>(); + for (Move legalMove : allLegalMoves) { + for (Coordinate destCoor : pseudoLegalCoordinates) { + Move pseudoLegalMove = new Move(this.position, destCoor, this); + if (pseudoLegalMove.equals(legalMove)) { + result.add(destCoor); } } - filteredCoordinates.add(filteredDirection.toArray(new Coordinate[0])); } - return filteredCoordinates.toArray(new Coordinate[0][0]); - } - public Coordinate[] getFlattenedFilteredCoordinates(ChessBoard board) { - Coordinate[][] filteredCoordinates = getFilteredCoordinates(board); - ArrayList flattenedCoordinates = new ArrayList<>(); - for (Coordinate[] direction : filteredCoordinates) { - for (Coordinate possibleCoord : direction) { - flattenedCoordinates.add(possibleCoord); - } - } - return flattenedCoordinates.toArray(new Coordinate[0]); + return result.toArray(new Coordinate[0]); } + //@@author public boolean isWhiteKing() { return this instanceof King && this.isWhite(); @@ -145,30 +147,25 @@ public void setEnPassant() { this.isEnPassant = true; } - //@@author ken-ruster public String[] getAvailableCoordinatesString(ChessBoard board) { StringBuilder out = new StringBuilder(); - Coordinate[][] availableCoordinates = getFilteredCoordinates(board); + Coordinate[] legalCoordinates = getLegalCoordinates(board); - boolean isEmpty = Arrays.stream(availableCoordinates).allMatch(x -> x.length == 0); - if (isEmpty) { + if (legalCoordinates.length == 0) { return new String[] { String.format(NO_AVAILABLE_MOVES_STRING, getPieceName(), this.position) }; } - for (Coordinate[] direction : availableCoordinates) { - for (Coordinate possibleCoord : direction) { - out.append(possibleCoord + " "); - } + for (Coordinate possibleCoord : legalCoordinates) { + out.append(possibleCoord + " "); } return new String[] { String.format(AVAILABLE_MOVES_STRING, getPieceName(), this.position), out.toString() }; - } //@@author onx001 diff --git a/src/main/java/chessmaster/pieces/EmptyPiece.java b/src/main/java/chessmaster/pieces/EmptyPiece.java index 9e4cf103de..95920fa9fc 100644 --- a/src/main/java/chessmaster/pieces/EmptyPiece.java +++ b/src/main/java/chessmaster/pieces/EmptyPiece.java @@ -13,8 +13,8 @@ public EmptyPiece(int row, int col) { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { - return new Coordinate[0][]; + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { + return new Coordinate[0]; } // An empty piece will never be moved diff --git a/src/main/java/chessmaster/pieces/King.java b/src/main/java/chessmaster/pieces/King.java index 9ec9bc61f8..f421aaaddc 100644 --- a/src/main/java/chessmaster/pieces/King.java +++ b/src/main/java/chessmaster/pieces/King.java @@ -33,7 +33,7 @@ public King(int row, int col, Color color) { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS.length][0]; for (int dir = 0; dir < DIRECTIONS.length; dir++) { @@ -82,7 +82,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { } } - return result; + return flattenArray(result); } @Override diff --git a/src/main/java/chessmaster/pieces/Knight.java b/src/main/java/chessmaster/pieces/Knight.java index 0e001fe205..ae1457660c 100644 --- a/src/main/java/chessmaster/pieces/Knight.java +++ b/src/main/java/chessmaster/pieces/Knight.java @@ -37,7 +37,7 @@ public String toString() { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS.length][0]; for (int dir = 0; dir < DIRECTIONS.length; dir++) { @@ -56,7 +56,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { } } - return result; + return flattenArray(result); } } diff --git a/src/main/java/chessmaster/pieces/Pawn.java b/src/main/java/chessmaster/pieces/Pawn.java index e9f58c3ea0..c6e88e0a22 100644 --- a/src/main/java/chessmaster/pieces/Pawn.java +++ b/src/main/java/chessmaster/pieces/Pawn.java @@ -35,9 +35,10 @@ public Pawn(int row, int col, Color color) { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS_UP.length][0]; int[][] directions = board.isPieceFriendly(this) ? DIRECTIONS_UP : DIRECTIONS_DOWN; + boolean canEnPassant = false; Coordinate enPassantCoor = null; @@ -91,7 +92,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { } } - return result; + return flattenArray(result); } @Override diff --git a/src/main/java/chessmaster/pieces/Queen.java b/src/main/java/chessmaster/pieces/Queen.java index 833bfb6202..ac6513e066 100644 --- a/src/main/java/chessmaster/pieces/Queen.java +++ b/src/main/java/chessmaster/pieces/Queen.java @@ -33,7 +33,7 @@ public Queen(int row, int col, Color color) { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS.length][0]; for (int dir = 0; dir < DIRECTIONS.length; dir++) { @@ -63,7 +63,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { result[dir] = possibleCoordInDirection.toArray(new Coordinate[0]); } - return result; + return flattenArray(result); } @Override diff --git a/src/main/java/chessmaster/pieces/Rook.java b/src/main/java/chessmaster/pieces/Rook.java index 71a74a7221..8f41b79b72 100644 --- a/src/main/java/chessmaster/pieces/Rook.java +++ b/src/main/java/chessmaster/pieces/Rook.java @@ -33,7 +33,7 @@ public Rook(int row, int col, Color color) { } @Override - public Coordinate[][] getPseudoCoordinates(ChessBoard board) { + public Coordinate[] getPseudoLegalCoordinates(ChessBoard board) { Coordinate[][] result = new Coordinate[DIRECTIONS.length][0]; for (int dir = 0; dir < DIRECTIONS.length; dir++) { @@ -63,7 +63,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) { result[dir] = possibleCoordInDirection.toArray(new Coordinate[0]); } - return result; + return flattenArray(result); } @Override diff --git a/src/main/java/chessmaster/storage/Storage.java b/src/main/java/chessmaster/storage/Storage.java index 89ede30815..e855f5aa8b 100644 --- a/src/main/java/chessmaster/storage/Storage.java +++ b/src/main/java/chessmaster/storage/Storage.java @@ -214,7 +214,7 @@ public void executeSavedMoves(Color playerColor, ChessBoard otherBoard, Human human, CPU cpu) throws ChessMasterException { - ArrayList moveStringList = new ArrayList(); + ArrayList moveStringList = new ArrayList(); ArrayList humanMoves = loadHumanMoves(); ArrayList cpuMoves = loadCPUMoves(); @@ -410,7 +410,7 @@ public ArrayList loadHumanMoves() throws ChessMasterException { } } - ArrayList out = new ArrayList(); + ArrayList out = new ArrayList(); if (fileScanner.hasNext()) { String[] movesArray = fileScanner.nextLine().split(", "); Arrays.stream(movesArray) @@ -419,6 +419,7 @@ public ArrayList loadHumanMoves() throws ChessMasterException { .forEach(x -> out.add(x)); } + fileScanner.close(); return out; } @@ -439,7 +440,7 @@ public ArrayList loadCPUMoves() throws ChessMasterException { } } - ArrayList out = new ArrayList(); + ArrayList out = new ArrayList(); if (fileScanner.hasNext()) { String[] movesArray = fileScanner.nextLine().split(", "); Arrays.stream(movesArray) @@ -449,7 +450,6 @@ public ArrayList loadCPUMoves() throws ChessMasterException { } fileScanner.close(); - return out; }