Skip to content

Commit

Permalink
Merge pull request #169 from TongZhengHong/branch-refactor-chessboard
Browse files Browse the repository at this point in the history
Refactor `ChessPiece` processing functions
  • Loading branch information
onx001 authored Nov 9, 2023
2 parents a57b464 + 201303c commit ab71546
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 92 deletions.
2 changes: 1 addition & 1 deletion src/main/java/chessmaster/commands/ShowMovesCommand.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
71 changes: 54 additions & 17 deletions src/main/java/chessmaster/game/ChessBoard.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -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<Move> allMoves = new ArrayList<>();

Expand All @@ -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));
}
Expand All @@ -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<Move> uncheckedMoves = new ArrayList<>();
Move[] moves = getPseudoLegalMoves(color);
ArrayList<Move> legalMoves = new ArrayList<>();

for (Move move : moves) {
ChessBoard newBoard = this.clone();
Coordinate from = move.getFrom();
Expand All @@ -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
Expand Down
15 changes: 8 additions & 7 deletions src/main/java/chessmaster/game/Move.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/chessmaster/pieces/Bishop.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -68,7 +68,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) {
result[dir] = possibleCoordInDirection.toArray(new Coordinate[0]);
}

return result;
return flattenArray(result);
}

@Override
Expand Down
95 changes: 46 additions & 49 deletions src/main/java/chessmaster/pieces/ChessPiece.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import chessmaster.game.Move;

import java.util.ArrayList;
import java.util.Arrays;

public abstract class ChessPiece {

Expand Down Expand Up @@ -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<Coordinate> 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<Coordinate[]> filteredCoordinates = new ArrayList<>();

for (Coordinate[] direction : getPseudoCoordinates(board)) {
ArrayList<Coordinate> 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<Coordinate> 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<Coordinate> 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();
Expand All @@ -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

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/chessmaster/pieces/EmptyPiece.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/chessmaster/pieces/King.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -82,7 +82,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) {
}
}

return result;
return flattenArray(result);
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/chessmaster/pieces/Knight.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand All @@ -56,7 +56,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) {
}
}

return result;
return flattenArray(result);
}

}
5 changes: 3 additions & 2 deletions src/main/java/chessmaster/pieces/Pawn.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -91,7 +92,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) {
}
}

return result;
return flattenArray(result);
}

@Override
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/chessmaster/pieces/Queen.java
Original file line number Diff line number Diff line change
Expand Up @@ -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++) {
Expand Down Expand Up @@ -63,7 +63,7 @@ public Coordinate[][] getPseudoCoordinates(ChessBoard board) {
result[dir] = possibleCoordInDirection.toArray(new Coordinate[0]);
}

return result;
return flattenArray(result);
}

@Override
Expand Down
Loading

0 comments on commit ab71546

Please sign in to comment.