787 lines
26 KiB
Java
787 lines
26 KiB
Java
package backend;
|
|
import java.util.ArrayList;
|
|
import java.util.Stack;
|
|
|
|
import javax.swing.JLabel;
|
|
import javax.swing.JPanel;
|
|
|
|
/*
|
|
Task: Chess Game Board Implementation
|
|
|
|
Class name: Board
|
|
|
|
Methods name:
|
|
- getWidth() - Returns the width of the board as an integer
|
|
- getHeight() - Returns the height of the board as an integer
|
|
- getTurnNumber() - Returns the current turn number as an integer
|
|
- isTurnWhite() - Returns true if it's white's turn, false if it's black's turn
|
|
- setPiece() - Places a piece on the board at specified coordinates, returns nothing
|
|
- populateBoard() - Sets up the initial chess position with all pieces, returns nothing
|
|
- cleanBoard() - Resets the board and turn counter to initial state, returns nothing
|
|
- toString() - Creates a string representation of the board with piece positions and turn info
|
|
- getPieces() - Returns ArrayList containing all pieces currently on the board
|
|
- userTouch() - Handles user interaction with the board (selecting/moving pieces), returns nothing
|
|
- isSelected() - Returns true if the specified square is currently selected, false otherwise
|
|
- toFileRep() - Returns a String array representation of the board for saving
|
|
- Board(String[] fileRepresentation) - Constructor that loads a board from string array, returns nothing
|
|
- calculateValidMoves() - Calculates and highlights valid moves for selected piece, returns nothing
|
|
- isHighlighted() - Returns true if the specified square is highlighted as a legal move
|
|
- clearHighlights() - Clears all highlighted squares, returns nothing
|
|
- highlightLegalMoves() - Highlights all legal moves for a given piece, returns nothing
|
|
- getLegalMoves() - Returns ArrayList of all legal moves for a given piece
|
|
- handlePawnMoves() - Adds all legal pawn moves to the moves list, returns nothing
|
|
- checkPawnCapture() - Checks if a pawn can capture diagonally, returns nothing
|
|
- handleKnightMoves() - Adds all legal knight moves to the moves list, returns nothing
|
|
- handleSlidingMoves() - Adds all legal sliding moves (bishop/rook/queen) to the moves list, returns nothing
|
|
- handleKingMoves() - Adds all legal king moves to the moves list, returns nothing
|
|
- isValidPosition() - Returns true if the coordinates are within the board boundaries
|
|
- isEmpty() - Returns true if the specified square is empty
|
|
- isEnemy() - Returns true if the specified square contains an enemy piece
|
|
- isAlly() - Returns true if the specified square contains a friendly piece
|
|
- undoLastMove() - Reverts to the previous board state, returns nothing
|
|
- Board(Board board) - Copy constructor that creates a duplicate board, returns nothing
|
|
- playMove() - Executes a chess move and updates the board state, returns nothing
|
|
- findKing() - Returns the Piece object of the king of specified color
|
|
- isKingInCheck() - Returns true if the king of specified color is in check
|
|
- promotePawn() - Returns a new Queen piece to replace a pawn at the end rank
|
|
- highlightKingInCheck() - Highlights kings that are in check, returns nothing
|
|
- canSelectPieceWhenInCheck() - Returns true if the piece can be selected when king is in check
|
|
- isSquareAttacked() - Returns true if the specified square is under attack by specified color
|
|
- filterMovesForCheck() - Removes moves that would leave king in check, returns nothing
|
|
|
|
Methods output:
|
|
- Board dimensions (width, height)
|
|
- Current turn number and active player
|
|
- String representation of the chess board
|
|
- Collection of all pieces on the board
|
|
- Selected piece status
|
|
- Highlighted squares showing legal moves
|
|
- File representation for saving/loading
|
|
- Move validation results
|
|
- King check status
|
|
- Game state information
|
|
|
|
Methods works:
|
|
- Board initialization and management
|
|
- Chess piece movement and validation
|
|
- Game state tracking (turns, check conditions)
|
|
- Special chess rules (promotion, en passant)
|
|
- Check and checkmate detection
|
|
- Move history and undo functionality
|
|
- Board state saving and loading
|
|
- Legal move calculation for all piece types
|
|
- User interaction handling
|
|
- Visual board representation
|
|
|
|
Authors: Bédier Jérôme jerome.bedier@ecam.fr, Gardern Florian florian.gardner@ecam.fr, Leng Sidden sidden.leng@ecam.fr
|
|
Date: 05/21/2025
|
|
*/
|
|
|
|
public class Board {
|
|
private Piece[][] board;
|
|
private int width;
|
|
private int height;
|
|
private int turn;
|
|
private int selectedX = -1;
|
|
private int selectedY = -1;
|
|
private boolean[][] highlightedSquares;
|
|
private Stack<String[]> undoStack = new Stack<>();
|
|
private int enPassantCol = -1;
|
|
private int enPassantRow = -1;
|
|
|
|
public Board(int colNum, int lineNum) {
|
|
this.width = colNum; // col move x
|
|
this.height = lineNum; // line mov in y
|
|
this.board = new Piece[width][height]; // 8x8 chess board
|
|
this.highlightedSquares = new boolean[width][height];
|
|
}
|
|
|
|
|
|
public int getWidth() {
|
|
//width = 8; // setting the width at 8 for the moment can be changed
|
|
return width;
|
|
}
|
|
|
|
public int getHeight() {
|
|
//height = 8; // setting the height at 8 for the moment can be changed
|
|
return height;
|
|
}
|
|
|
|
public int getTurnNumber() {
|
|
return turn;
|
|
}
|
|
|
|
public boolean isTurnWhite() {
|
|
return turn % 2 == 0; // White starts on turn 0 and only even turns
|
|
}
|
|
|
|
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
|
/* Ensure coordinates are inside the board boundaries
|
|
if (x < 0 || x >= width || y < 0 || y >= height) {
|
|
System.out.println("Invalid coordinates: (" + x + ", " + y + ")");
|
|
return;
|
|
}*/
|
|
|
|
// Create and place the new piece
|
|
board[x][y] = new Piece(isWhite, type, x, y);
|
|
System.out.println(toString()); // Work but not sure ?
|
|
}
|
|
|
|
|
|
public void populateBoard() {
|
|
// Place Rooks
|
|
board[0][0] = new Piece(false, PieceType.Rook, 0, 0); //color, piece , x -> , y |
|
|
board[0][7] = new Piece(true, PieceType.Rook, 0, 7);
|
|
board[7][0] = new Piece(false, PieceType.Rook, 7, 0);
|
|
board[7][7] = new Piece(true, PieceType.Rook, 7, 7);
|
|
// Place Knights
|
|
board[1][0] = new Piece(false, PieceType.Knight, 1, 0);
|
|
board[1][7] = new Piece(true, PieceType.Knight, 1, 7);
|
|
board[6][0] = new Piece(false, PieceType.Knight, 6, 0);
|
|
board[6][7] = new Piece(true, PieceType.Knight, 6, 7);
|
|
// Place Bishops
|
|
board[2][0] = new Piece(false, PieceType.Bishop, 2, 0);
|
|
board[2][7] = new Piece(true, PieceType.Bishop, 2, 7);
|
|
board[5][0] = new Piece(false, PieceType.Bishop, 5, 0);
|
|
board[5][7] = new Piece(true, PieceType.Bishop, 5, 7);
|
|
// Place Queens
|
|
board[4][0] = new Piece(false, PieceType.Queen, 4, 0);
|
|
board[4][7] = new Piece(true, PieceType.Queen, 4, 7);
|
|
// Place Kings
|
|
board[3][0] = new Piece(false, PieceType.King, 3, 0);
|
|
board[3][7] = new Piece(true, PieceType.King, 3, 7);
|
|
// Place Pawns
|
|
for (int i = 0; i < 8; i++) {
|
|
board[i][1] = new Piece(false, PieceType.Pawn, i, 1); //color, piece , x -> , y |
|
|
board[i][6] = new Piece(true, PieceType.Pawn, i, 6); //color, piece , x -> , y |
|
|
}
|
|
}
|
|
|
|
public void cleanBoard() {
|
|
turn = 0;
|
|
this.board = new Piece[width][height]; // should work ?
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
Piece piece = board[x][y];
|
|
if (piece == null) {
|
|
sb.append(" ");
|
|
} else {
|
|
// Using Teacher method no breaks
|
|
sb.append(piece.isWhite() ? "W" : "B")
|
|
.append(piece.getType().getSummary());
|
|
}
|
|
if (x < width - 1) sb.append(",");
|
|
}
|
|
sb.append("\n");
|
|
}
|
|
sb.append("Turn: ").append(isTurnWhite() ? "White" : "Black").append("\n");
|
|
return sb.toString();
|
|
}
|
|
|
|
public ArrayList<Piece> getPieces() {
|
|
ArrayList<Piece> pieces = new ArrayList<>();
|
|
for (int i = 0; i < width; i++) {
|
|
for (int j = 0; j < height; j++) {
|
|
if (board[i][j] != null) {
|
|
pieces.add(board[i][j]);
|
|
}
|
|
}
|
|
}
|
|
|
|
return pieces;
|
|
}
|
|
|
|
|
|
|
|
public void userTouch(int x, int y) {
|
|
if (x < 0 || x >= width || y < 0 || y >= height) return;
|
|
|
|
Piece clickedPiece = board[x][y];
|
|
|
|
// No piece selected yet
|
|
if (selectedX == -1 && selectedY == -1) {
|
|
if (clickedPiece != null && clickedPiece.isWhite() == isTurnWhite() && canSelectPieceWhenInCheck(x, y)){
|
|
selectedX = x;
|
|
selectedY = y;
|
|
calculateValidMoves(clickedPiece);
|
|
filterMovesForCheck();
|
|
}
|
|
} else {
|
|
// Clicked the same piece again : unselect
|
|
if (x == selectedX && y == selectedY) {
|
|
selectedX = -1;
|
|
selectedY = -1;
|
|
clearHighlights();
|
|
|
|
} else if (highlightedSquares[x][y]) {
|
|
// Move the piece
|
|
Move move = new Move(selectedX, selectedY, x, y);
|
|
playMove(move); // This handles moving the piece AND saving the undo state.
|
|
selectedX = -1;
|
|
selectedY = -1; // Unselect
|
|
|
|
clearHighlights();
|
|
highlightKingInCheck();
|
|
System.out.println(toString());
|
|
}
|
|
}
|
|
}
|
|
|
|
public boolean isSelected(int x, int y) {
|
|
return x == selectedX && y == selectedY;
|
|
}
|
|
|
|
/* saving-loading feature :*/
|
|
|
|
public String[] toFileRep() {
|
|
String[] lines = new String[height + 1]; // one line per row + 1 for turn
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int x = 0; x < width; x++) {
|
|
Piece piece = board[x][y];
|
|
if (piece == null) {
|
|
sb.append("--");
|
|
} else {
|
|
sb.append(piece.isWhite() ? "W" : "B");
|
|
sb.append(piece.getType().getSummary());
|
|
}
|
|
if (x < width - 1) sb.append(",");
|
|
}
|
|
lines[y] = sb.toString();
|
|
}
|
|
|
|
// Last line stores the turn number
|
|
lines[height] = Integer.toString(turn);
|
|
|
|
return lines;
|
|
}
|
|
|
|
|
|
public Board(String[] fileRepresentation) {
|
|
this.height = fileRepresentation.length - 1;
|
|
this.width = fileRepresentation[0].split(",").length;
|
|
this.board = new Piece[width][height];
|
|
this.highlightedSquares = new boolean[width][height];
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
String[] tokens = fileRepresentation[y].split(",");
|
|
for (int x = 0; x < width; x++) {
|
|
String token = tokens[x];
|
|
if (!token.equals("--")) {
|
|
boolean isWhite = token.charAt(0) == 'W';
|
|
char pieceChar = token.charAt(1);
|
|
PieceType type = PieceType.fromSummary(pieceChar);
|
|
board[x][y] = new Piece(isWhite, type, x, y);
|
|
} else {
|
|
board[x][y] = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.turn = Integer.parseInt(fileRepresentation[height]);
|
|
}
|
|
|
|
|
|
private void calculateValidMoves(Piece piece) {
|
|
clearHighlights();
|
|
if (piece == null) return;
|
|
ArrayList<Move> legalMoves = getLegalMoves(piece); // Make sure getLegalMoves is implemented
|
|
for (Move move : legalMoves) {
|
|
highlightedSquares[move.getToX()][move.getToY()] = true;
|
|
}
|
|
}
|
|
/* The following methods require more work ! */
|
|
|
|
public boolean isHighlighted(int x, int y) {
|
|
return highlightedSquares[x][y];
|
|
}
|
|
|
|
private void clearHighlights() {
|
|
highlightedSquares = new boolean[width][height];
|
|
}
|
|
|
|
private void highlightLegalMoves(Piece piece) {
|
|
// Clear previous highlights
|
|
for (int i = 0; i < width; i++)
|
|
for (int j = 0; j < height; j++)
|
|
highlightedSquares[i][j] = false;
|
|
|
|
if (piece == null) return;
|
|
|
|
ArrayList<Move> legalMoves = getLegalMoves(piece); // your existing method
|
|
for (int i = 0; i < legalMoves.size(); i++) {
|
|
Move move = legalMoves.get(i);
|
|
highlightedSquares[move.getToX()][move.getToY()] = true; // assuming Move has toX and toY fields
|
|
}
|
|
}
|
|
|
|
public ArrayList<Move> getLegalMoves(Piece piece) {
|
|
ArrayList<Move> moves = new ArrayList<>();
|
|
int x = piece.getX();
|
|
int y = piece.getY();
|
|
boolean isWhite = piece.isWhite();
|
|
|
|
switch(piece.getType()) {
|
|
case Pawn:
|
|
handlePawnMoves(moves, piece, x, y);
|
|
break;
|
|
case Knight:
|
|
handleKnightMoves(moves, piece, x, y);
|
|
break;
|
|
case Bishop:
|
|
handleSlidingMoves(moves, piece, x, y, new int[][]{{1,1}, {1,-1}, {-1,1}, {-1,-1}});
|
|
break;
|
|
case Rook:
|
|
handleSlidingMoves(moves, piece, x, y, new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}});
|
|
break;
|
|
case Queen:
|
|
handleSlidingMoves(moves, piece, x, y, new int[][]{{1,0}, {-1,0}, {0,1}, {0,-1}, {1,1}, {1,-1}, {-1,1}, {-1,-1}});
|
|
break;
|
|
case King:
|
|
handleKingMoves(moves, piece, x, y);
|
|
break;
|
|
}
|
|
return moves;
|
|
}
|
|
|
|
// Helper methods for movement calculations
|
|
private void handlePawnMoves(ArrayList<Move> moves, Piece pawn, int x, int y) {
|
|
int direction = pawn.isWhite() ? -1 : 1;
|
|
int startRow = pawn.isWhite() ? 6 : 1;
|
|
|
|
// Forward moves
|
|
if (isEmpty(x, y + direction)) {
|
|
moves.add(new Move(x, y, x, y + direction));
|
|
if (y == startRow && isEmpty(x, y + 2*direction)) {
|
|
moves.add(new Move(x, y, x, y + 2*direction));
|
|
}
|
|
}
|
|
|
|
// Captures
|
|
checkPawnCapture(moves, pawn, x, y, direction, -1);
|
|
checkPawnCapture(moves, pawn, x, y, direction, 1);
|
|
|
|
// En passant captures
|
|
if ((pawn.isWhite() && y == 3) || (!pawn.isWhite() && y == 4)) {
|
|
// Check both sides
|
|
for (int dx : new int[]{-1, 1}) {
|
|
int captureX = x + dx;
|
|
// If there's a valid en passant opportunity
|
|
if (captureX == enPassantCol && y + direction == enPassantRow) {
|
|
Move enPassantMove = new Move(x, y, captureX, y + direction);
|
|
// Set the captured piece (the pawn that just moved)
|
|
enPassantMove.setCapturedPiece(board[captureX][y]);
|
|
moves.add(enPassantMove);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void checkPawnCapture(ArrayList<Move> moves, Piece pawn, int x, int y, int dir, int dx) {
|
|
int nx = x + dx;
|
|
int ny = y + dir;
|
|
if (isEnemy(nx, ny, pawn.isWhite())) {
|
|
Move move = new Move(x, y, nx, ny);
|
|
move.setCapturedPiece(board[nx][ny]);
|
|
moves.add(move);
|
|
}
|
|
}
|
|
|
|
private void handleKnightMoves(ArrayList<Move> moves, Piece knight, int x, int y) {
|
|
int[][] offsets = {{2,1}, {2,-1}, {-2,1}, {-2,-1},
|
|
{1,2}, {1,-2}, {-1,2}, {-1,-2}};
|
|
for (int[] offset : offsets) {
|
|
int nx = x + offset[0];
|
|
int ny = y + offset[1];
|
|
if (isValidPosition(nx, ny) && !isAlly(nx, ny, knight.isWhite())) {
|
|
Move move = new Move(x, y, nx, ny);
|
|
if (isEnemy(nx, ny, knight.isWhite())) {
|
|
move.setCapturedPiece(board[nx][ny]);
|
|
}
|
|
moves.add(move);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleSlidingMoves(ArrayList<Move> moves, Piece piece, int x, int y, int[][] directions) {
|
|
for (int[] dir : directions) {
|
|
int nx = x;
|
|
int ny = y;
|
|
while (true) {
|
|
nx += dir[0];
|
|
ny += dir[1];
|
|
if (!isValidPosition(nx, ny)) break;
|
|
if (isAlly(nx, ny, piece.isWhite())) break;
|
|
|
|
Move move = new Move(x, y, nx, ny);
|
|
if (isEnemy(nx, ny, piece.isWhite())) {
|
|
move.setCapturedPiece(board[nx][ny]);
|
|
moves.add(move);
|
|
break;
|
|
}
|
|
moves.add(move);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void handleKingMoves(ArrayList<Move> moves, Piece king, int x, int y) {
|
|
for (int dx = -1; dx <= 1; dx++) {
|
|
for (int dy = -1; dy <= 1; dy++) {
|
|
if (dx == 0 && dy == 0) continue;
|
|
int nx = x + dx;
|
|
int ny = y + dy;
|
|
if (isValidPosition(nx, ny) && !isAlly(nx, ny, king.isWhite())) {
|
|
Move move = new Move(x, y, nx, ny);
|
|
if (isEnemy(nx, ny, king.isWhite())) {
|
|
move.setCapturedPiece(board[nx][ny]);
|
|
}
|
|
moves.add(move);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Utility methods
|
|
private boolean isValidPosition(int x, int y) {
|
|
return x >= 0 && x < width && y >= 0 && y < height;
|
|
}
|
|
|
|
public boolean isEmpty(int x, int y) {
|
|
return isValidPosition(x, y) && board[x][y] == null;
|
|
}
|
|
|
|
private boolean isEnemy(int x, int y, boolean isWhite) {
|
|
return isValidPosition(x, y) && board[x][y] != null && board[x][y].isWhite() != isWhite;
|
|
}
|
|
|
|
private boolean isAlly(int x, int y, boolean isWhite) {
|
|
return isValidPosition(x, y) && board[x][y] != null && board[x][y].isWhite() == isWhite;
|
|
}
|
|
|
|
public Piece getPieceAt(int x, int y) {
|
|
if (!isValidPosition(x, y)) return null;
|
|
return board[x][y];
|
|
}
|
|
|
|
public void removePieceAt(int x, int y) {
|
|
if (isValidPosition(x, y)) board[x][y] = null;
|
|
}
|
|
|
|
public void setEnPassantCol(int col) {
|
|
this.enPassantCol = col;
|
|
}
|
|
|
|
public void setEnPassantRow(int row) {
|
|
this.enPassantRow = row;
|
|
}
|
|
|
|
public int getEnPassantCol() {
|
|
return this.enPassantCol;
|
|
}
|
|
|
|
public int getEnPassantRow() {
|
|
return this.enPassantRow;
|
|
}
|
|
|
|
|
|
|
|
public void undoLastMove() {
|
|
if (!undoStack.isEmpty()) {
|
|
String[] lastState = undoStack.pop();
|
|
|
|
// Load the board from the saved state
|
|
Board previousBoard = new Board(lastState);
|
|
|
|
// Copy the previousBoard's fields into current board instance
|
|
this.board = previousBoard.board;
|
|
this.turn = previousBoard.turn;
|
|
this.width = previousBoard.width;
|
|
this.height = previousBoard.height;
|
|
this.highlightedSquares = previousBoard.highlightedSquares;
|
|
|
|
// Clear selection and highlights as well (optional)
|
|
this.selectedX = -1;
|
|
this.selectedY = -1;
|
|
clearHighlights();
|
|
|
|
System.out.println("Undo performed.");
|
|
System.out.println(this.toString());
|
|
} else {
|
|
System.out.println("No moves to undo.");
|
|
}
|
|
}
|
|
|
|
|
|
public Board(Board original) {
|
|
this.width = original.width;
|
|
this.height = original.height;
|
|
this.turn = original.turn;
|
|
this.selectedX = original.selectedX;
|
|
this.selectedY = original.selectedY;
|
|
this.enPassantCol = original.enPassantCol;
|
|
this.enPassantRow = original.enPassantRow;
|
|
|
|
// Deep copy the board array and pieces
|
|
this.board = new Piece[width][height];
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
Piece p = original.board[x][y];
|
|
if (p != null) {
|
|
this.board[x][y] = new Piece(p.isWhite(), p.getType(), x, y);
|
|
} else {
|
|
this.board[x][y] = null;
|
|
}
|
|
}
|
|
}
|
|
// Deep copy highlightedSquares
|
|
this.highlightedSquares = new boolean[width][height];
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
this.highlightedSquares[x][y] = original.highlightedSquares[x][y];
|
|
}
|
|
}
|
|
|
|
// Undo stack is not copied (empty)
|
|
this.undoStack = new Stack<>();
|
|
}//test
|
|
|
|
|
|
public void playMove(Move move) {
|
|
// Save current state before move for undo
|
|
undoStack.push(toFileRep());
|
|
|
|
Piece piece = board[move.getFromX()][move.getFromY()];
|
|
SpecialMoves specialMoves = new SpecialMoves();
|
|
|
|
// Handle en passant capture
|
|
if (specialMoves.isEnPassant(this, move)) {
|
|
Piece capturedPawn = board[move.getToX()][move.getFromY()];
|
|
move.setCapturedPiece(capturedPawn);
|
|
specialMoves.handleEnPassant(this, move);
|
|
}
|
|
|
|
// Move the piece
|
|
board[move.getToX()][move.getToY()] = piece;
|
|
board[move.getFromX()][move.getFromY()] = null;
|
|
piece.x = move.getToX();
|
|
piece.y = move.getToY();
|
|
|
|
// Update en passant tracking
|
|
specialMoves.updateEnPassantTracking(this, move, piece);
|
|
|
|
// Handle pawn promotion
|
|
if (piece.getType() == PieceType.Pawn &&
|
|
(piece.getY() == 0 || piece.getY() == height - 1)) {
|
|
board[piece.getX()][piece.getY()] = specialMoves.promotePawn(piece.isWhite(), piece.getX(), piece.getY());
|
|
}
|
|
|
|
turn++;
|
|
}
|
|
|
|
|
|
|
|
//Finds the position of the king of a given color.
|
|
public Piece findKing(boolean isWhite) {
|
|
for (int x = 0; x < width; x++) {
|
|
for (int y = 0; y < height; y++) {
|
|
Piece piece = board[x][y];
|
|
if (piece != null && piece.getType() == PieceType.King && piece.isWhite() == isWhite) {
|
|
return piece;
|
|
}
|
|
}
|
|
}
|
|
return null; // King not found (should not happen in normal chess)
|
|
}
|
|
|
|
// check king check based on a given color
|
|
public boolean isKingInCheck(boolean isWhite) {
|
|
// First, find the king's position
|
|
Piece king = findKing(isWhite);
|
|
soudEffect soundPlayer = new soudEffect();
|
|
|
|
if (king == null) {
|
|
// If king not found (shouldn't happen in a valid chess game)
|
|
return false;
|
|
}
|
|
|
|
int kingX = king.getX();
|
|
int kingY = king.getY();
|
|
|
|
// Check if any enemy piece can attack the king
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
Piece piece = board[x][y];
|
|
|
|
// Skip empty squares and pieces of the same color as the king
|
|
if (piece == null || piece.isWhite() == isWhite) {
|
|
continue;
|
|
}
|
|
|
|
// Get all legal moves for this enemy piece
|
|
ArrayList<Move> moves = getLegalMoves(piece);
|
|
|
|
// Check if any move can capture the king
|
|
for (Move move : moves) {
|
|
if (move.getToX() == kingX && move.getToY() == kingY) {
|
|
soundPlayer.playCheckSound();
|
|
return true; // King is in check
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false; // King is not in check
|
|
}
|
|
|
|
public Piece promotePawn(boolean isWhite, int x, int y) {
|
|
return new Piece(isWhite, PieceType.Queen, x, y);
|
|
}
|
|
|
|
private void highlightKingInCheck() {
|
|
int highlightedCount = 0;
|
|
// Check if white king is in check
|
|
Piece whiteKing = findKing(true);
|
|
if (whiteKing != null && isKingInCheck(true)) {
|
|
highlightedSquares[whiteKing.getX()][whiteKing.getY()] = true;
|
|
}
|
|
|
|
//check if white king checkmate
|
|
|
|
|
|
// Check if black king is in check
|
|
Piece blackKing = findKing(false);
|
|
if (blackKing != null && isKingInCheck(false)) {
|
|
highlightedSquares[blackKing.getX()][blackKing.getY()] = true;
|
|
}
|
|
|
|
} // check
|
|
|
|
public boolean canSelectPieceWhenInCheck(int x, int y) {
|
|
Piece piece = board[x][y];
|
|
|
|
// If no piece at this position or wrong color, can't select
|
|
if (piece == null || piece.isWhite() != isTurnWhite()) {
|
|
return false;
|
|
}
|
|
|
|
// If king is not in check, any piece can be selected
|
|
if (!isKingInCheck(isTurnWhite())) {
|
|
return true;
|
|
}
|
|
|
|
// When king is in check, check if this piece can make any legal move
|
|
// that would get the king out of check
|
|
ArrayList<Move> legalMoves = getLegalMoves(piece);
|
|
for (Move move : legalMoves) {
|
|
// Temporarily make the move
|
|
Piece capturedPiece = board[move.getToX()][move.getToY()];
|
|
board[move.getToX()][move.getToY()] = piece;
|
|
board[move.getFromX()][move.getFromY()] = null;
|
|
|
|
// Check if king is still in check after this move
|
|
boolean stillInCheck = isKingInCheck(isTurnWhite());
|
|
|
|
// Undo the move
|
|
board[move.getFromX()][move.getFromY()] = piece;
|
|
board[move.getToX()][move.getToY()] = capturedPiece;
|
|
|
|
// If this move gets king out of check, piece can be selected
|
|
if (!stillInCheck) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// No legal moves that get king out of check
|
|
return false;
|
|
}
|
|
|
|
public boolean isSquareAttacked(int x, int y, boolean byWhite) {
|
|
for (int i = 0; i < width; i++) {
|
|
for (int j = 0; j < height; j++) {
|
|
Piece attacker = board[i][j];
|
|
if (attacker != null && attacker.isWhite() == byWhite) {
|
|
ArrayList<Move> moves = getLegalMoves(attacker);
|
|
for (Move move : moves) {
|
|
if (move.getToX() == x && move.getToY() == y) {
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private void filterMovesForCheck() {
|
|
Piece selectedPiece = board[selectedX][selectedY];
|
|
boolean[][] newHighlights = new boolean[width][height];
|
|
|
|
for (int y = 0; y < height; y++) {
|
|
for (int x = 0; x < width; x++) {
|
|
if (highlightedSquares[x][y]) {
|
|
// Temporarily make the move
|
|
Piece capturedPiece = board[x][y];
|
|
board[x][y] = selectedPiece;
|
|
board[selectedX][selectedY] = null;
|
|
|
|
// Check if king is in check after the move
|
|
boolean leavesKingInCheck;
|
|
|
|
// If moving the king, check if the destination square is attacked
|
|
if (selectedPiece.getType() == PieceType.King) {
|
|
leavesKingInCheck = isSquareAttacked(x, y, !selectedPiece.isWhite());
|
|
System.out.println("Game Over"); // only work if king selected
|
|
}
|
|
|
|
else {
|
|
leavesKingInCheck = isKingInCheck(isTurnWhite());
|
|
}
|
|
// Undo the move
|
|
board[selectedX][selectedY] = selectedPiece;
|
|
board[x][y] = capturedPiece;
|
|
|
|
// Keep only safe moves
|
|
if (!leavesKingInCheck) {
|
|
newHighlights[x][y] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Replace highlights with filtered version
|
|
highlightedSquares = newHighlights;
|
|
}
|
|
|
|
public boolean isCheckmate(boolean isWhite) {
|
|
if (!isKingInCheck(isWhite)) {
|
|
return false; // Not in check, so not checkmate
|
|
}
|
|
|
|
// Check if ANY legal move can get the king out of check
|
|
for (Piece piece : getPieces()) {
|
|
if (piece.isWhite() == isWhite) {
|
|
ArrayList<Move> legalMoves = getLegalMoves(piece);
|
|
|
|
for (Move move : legalMoves) {
|
|
// Simulate move
|
|
Board simulatedBoard = new Board(this.toFileRep());
|
|
simulatedBoard.playMove(move);
|
|
|
|
// If king is not in check after this move, it's not checkmate
|
|
if (!simulatedBoard.isKingInCheck(isWhite)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true; // No legal move prevents check => checkmate
|
|
}
|
|
|
|
}
|