517 lines
16 KiB
Java
517 lines
16 KiB
Java
package backend;
|
||
|
||
import java.util.ArrayList;
|
||
|
||
|
||
public class Board {
|
||
private int colNum;
|
||
private int lineNum;
|
||
private int turnNumber;
|
||
private boolean isWhiteTurn;
|
||
ArrayList<Piece> pieces = new ArrayList<>();
|
||
private int selectedX = -1;
|
||
private int selectedY = -1;
|
||
ArrayList<int[]> highlightedSquares = new ArrayList<>();
|
||
private boolean pawnDoubleStep;
|
||
private int xCoordinatePawn;
|
||
private int yCoordinatePawn;
|
||
|
||
private ArrayList<Board> previousStates;
|
||
|
||
public Board(int colNum, int lineNum) {
|
||
this.colNum = colNum;
|
||
this.lineNum = lineNum;
|
||
this.turnNumber = 0;
|
||
this.isWhiteTurn = true; // White starts first
|
||
this.previousStates = new ArrayList<>(); // Initialize the ArrayList
|
||
}
|
||
|
||
public int getWidth() {
|
||
return colNum;
|
||
}
|
||
|
||
public boolean isPawnDoubleStep() {
|
||
return pawnDoubleStep;
|
||
}
|
||
|
||
public int getXCoordinatePawn() {
|
||
return xCoordinatePawn;
|
||
}
|
||
|
||
public int getYCoordinatePawn() {
|
||
return yCoordinatePawn;
|
||
}
|
||
|
||
public int getHeight() {
|
||
return lineNum;
|
||
}
|
||
|
||
public int getTurnNumber() {
|
||
return this.turnNumber;
|
||
}
|
||
|
||
public boolean isTurnWhite() {
|
||
return this.isWhiteTurn;
|
||
}
|
||
|
||
public void resetTurn() {
|
||
this.turnNumber = 0;
|
||
this.isWhiteTurn = true;
|
||
}
|
||
|
||
public void setPiece(boolean isWhite, PieceType type, int x, int y) {
|
||
Piece newPiece = new Piece(x, y, isWhite, type);
|
||
pieces.add(newPiece);
|
||
}
|
||
|
||
public void populateBoard() {
|
||
for (int y=0;y<8;y++) {
|
||
for(int x=0;x<8;x++) {
|
||
if(x==0||x==7) {
|
||
if (y==0) {
|
||
pieces.add(new Piece(x,y,false, PieceType.Rook));
|
||
}
|
||
if (y==7) {
|
||
pieces.add(new Piece(x,y,true, PieceType.Rook));
|
||
}
|
||
}
|
||
if(x==1||x==6) {
|
||
if (y==0) {
|
||
pieces.add(new Piece(x,y,false, PieceType.Knight));
|
||
}
|
||
if (y==7) {
|
||
pieces.add(new Piece(x,y,true, PieceType.Knight));
|
||
}
|
||
}
|
||
if(x==2||x==5) {
|
||
if (y==0) {
|
||
pieces.add(new Piece(x,y,false, PieceType.Bishop));
|
||
}
|
||
if (y==7) {
|
||
pieces.add(new Piece(x,y,true, PieceType.Bishop));
|
||
}
|
||
}
|
||
if(x==3) {
|
||
if (y==0) {
|
||
pieces.add(new Piece(x,y,false, PieceType.Queen));
|
||
}
|
||
if (y==7) {
|
||
pieces.add(new Piece(x,y,true, PieceType.Queen));
|
||
}
|
||
}
|
||
if(x==4) {
|
||
if (y==0) {
|
||
pieces.add(new Piece(x,y,false, PieceType.King));
|
||
}
|
||
if (y==7) {
|
||
pieces.add(new Piece(x,y,true, PieceType.King));
|
||
}
|
||
}
|
||
if(y==1) {
|
||
pieces.add(new Piece(x,y,false, PieceType.Pawn));
|
||
}
|
||
if(y==6) {
|
||
pieces.add(new Piece(x,y,true, PieceType.Pawn));
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
public void cleanBoard() {
|
||
pieces.clear();
|
||
}
|
||
|
||
public String toString() {
|
||
String result = "Turn: " + turnNumber + "\n";
|
||
result += "Current Player: " + (isWhiteTurn ? "White" : "Black") + "\n";
|
||
result += "Pieces on Board:\n";
|
||
|
||
for (Piece piece : pieces) {
|
||
result += piece + "\n";
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public ArrayList<Piece> getPieces() {
|
||
return pieces;
|
||
}
|
||
|
||
public Piece getPieceAt(int x, int y) {
|
||
Piece foundPiece = null;
|
||
for (int i = 0; i < pieces.size(); i++) {
|
||
Piece piece = pieces.get(i);
|
||
if (piece.getX() == x && piece.getY() == y) {
|
||
foundPiece = piece;
|
||
}
|
||
}
|
||
return foundPiece;
|
||
}
|
||
|
||
public void userTouch(int x, int y) {
|
||
System.out.println("userTouch triggered at: " + x + ", " + y);
|
||
|
||
Piece selectedPiece = getPieceAt(selectedX, selectedY);
|
||
Piece clickedPiece = getPieceAt(x, y);
|
||
|
||
if (selectedPiece == null) {
|
||
if (clickedPiece != null && clickedPiece.isWhite() == isWhiteTurn) {
|
||
System.out.println("Selecting piece at: " + x + ", " + y);
|
||
selectedX = x;
|
||
selectedY = y;
|
||
highlightedSquares = getValidMoves(clickedPiece);
|
||
System.out.println("Valid moves highlighted for selected piece.");
|
||
} else {
|
||
System.out.println("No valid piece to select at: " + x + ", " + y);
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (x == selectedX && y == selectedY) {
|
||
System.out.println("Unselecting piece at: " + x + ", " + y);
|
||
selectedX = -1;
|
||
selectedY = -1;
|
||
highlightedSquares.clear();
|
||
return;
|
||
}
|
||
|
||
// Check if move is valid (square must be highlighted to avoid our previous problem of moving a piece to any square on the board)
|
||
boolean isValidMove = false;
|
||
for (int[] move : highlightedSquares) {
|
||
if (move[0] == x && move[1] == y) {
|
||
isValidMove = true;
|
||
}
|
||
}
|
||
|
||
if (!isValidMove) {
|
||
System.out.println("Invalid move — not in highlighted squares.");
|
||
return;
|
||
}
|
||
|
||
//store for castling
|
||
int originalX = selectedPiece.getX();
|
||
int originalY = selectedPiece.getY();
|
||
|
||
// detects castling
|
||
boolean isCastling = selectedPiece.getType() == PieceType.King && Math.abs(x - originalX) == 2;
|
||
|
||
|
||
|
||
if (clickedPiece != null) {
|
||
System.out.println("Capturing piece at: " + x + ", " + y);
|
||
pieces.remove(clickedPiece);
|
||
}
|
||
|
||
System.out.println("Moving piece to: " + x + ", " + y);
|
||
selectedPiece.setX(x);
|
||
selectedPiece.setY(y);
|
||
|
||
if (isCastling) {//checks if the king is eligible for castling
|
||
int row = selectedPiece.isWhite() ? 7 : 0;//deduces the row depending on weither the selected king is on white or not (if white, its in row 7, if its black its row 0)
|
||
if (x > originalX) {// means we are moving the king towards the right (king side castling)
|
||
Piece rook = getPieceAt(7, row);//selects the correct rook to be castled depending on weither the king went right or left (here selects right rook)
|
||
if (rook != null) {//if the rook is there
|
||
rook.setX(5);//moves the rook to the square to the left of the king
|
||
rook.setY(row);//same row
|
||
rook.setMoved(true);//the rook has been moved once so wont be used for castling anymore
|
||
}
|
||
} else {//king moves to the left this time (queen side castling)
|
||
Piece rook = getPieceAt(0, row);//selects the rook on the left
|
||
if (rook != null) {
|
||
rook.setX(3);//moves rook to the right of the king
|
||
rook.setY(row);
|
||
rook.setMoved(true);
|
||
}
|
||
}
|
||
}
|
||
|
||
if (selectedPiece.getType() == PieceType.Pawn && Math.abs(y - selectedY) == 2) {
|
||
pawnDoubleStep = true; //boolean to check if pawn has been moved 2 at start
|
||
xCoordinatePawn = x; //get its coordinates
|
||
yCoordinatePawn = y;
|
||
System.out.println("Pawn moved two squares to (" + xCoordinatePawn + ", " + yCoordinatePawn + ")");
|
||
} else {
|
||
pawnDoubleStep = false;
|
||
}
|
||
|
||
turnNumber++;
|
||
isWhiteTurn = !isWhiteTurn;
|
||
|
||
selectedX = -1;
|
||
selectedY = -1;
|
||
highlightedSquares.clear();
|
||
|
||
// After move completed, check for check and checkmate
|
||
for (int i = 0; i < 2; i++) {
|
||
boolean isWhite = (i == 0);
|
||
|
||
if (isKingInCheck(isWhite)) {
|
||
System.out.println((isWhite ? "White" : "Black") + " is in check!");
|
||
|
||
if (isCheckmate(isWhite)) {
|
||
System.out.println((isWhite ? "White" : "Black") + " is in checkmate!");
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
}
|
||
|
||
public boolean isSelected(int x, int y) {
|
||
return x == selectedX && y == selectedY;
|
||
}
|
||
|
||
/* saving-loading feature :*/
|
||
//this public method returns String[]
|
||
public String[] toFileRep() { //converts the game into a chain of characters : state of the game
|
||
ArrayList<String> lines = new ArrayList<>();//creates a list arraylist to store each line of the save data as a string
|
||
|
||
//number of tour
|
||
lines.add(String.valueOf(turnNumber));//It's converted to a string using String.valueOf
|
||
|
||
//color of the player
|
||
lines.add(String.valueOf(isWhiteTurn));//same
|
||
|
||
// piece type position and color
|
||
for (Piece piece : pieces) { //loop through all pieces of the game
|
||
String line = piece.getType() + "," + piece.getX() + "," + piece.getY() + "," + piece.isWhite();
|
||
lines.add(line);
|
||
}
|
||
|
||
return lines.toArray(new String[0]); //Converts the ArrayList to a fixed-size String[] and returns it
|
||
}
|
||
|
||
//constructor for the Board class.
|
||
public Board(String[] array) { //takes the previous string and reconstruct the game state from it
|
||
this.colNum = 8;//dimensions initialized
|
||
this.lineNum = 8;
|
||
|
||
this.turnNumber = Integer.parseInt(array[0]);//array[0] is the turn number (converted from string to int),
|
||
|
||
this.isWhiteTurn = Boolean.parseBoolean(array[1]);//array[1] is the current player’s turn (converted from string to boolean).
|
||
|
||
this.pieces = new ArrayList<>();//initialize empty list to hold all pieces
|
||
|
||
for (int i = 2; i < array.length; i++) {
|
||
|
||
String[] parts = array[i].split(",");
|
||
|
||
PieceType type = PieceType.valueOf(parts[0]);
|
||
int x = Integer.parseInt(parts[1]);
|
||
int y = Integer.parseInt(parts[2]);
|
||
boolean isWhite = Boolean.parseBoolean(parts[3]);
|
||
|
||
pieces.add(new Piece(x, y, isWhite, type)); //Creates a new Piece with the extracted data and adds it to the pieces list.
|
||
}
|
||
}
|
||
|
||
/* The following methods require more work ! */
|
||
|
||
public boolean isHighlighted(int x, int y) {
|
||
for (int[] pos : highlightedSquares) {
|
||
if (pos[0] == x && pos[1] == y) {
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
public void getLastMove (int x, int y) {// it saves the current state before making a move
|
||
|
||
previousStates.add(new Board(this)); // Use the existing constructor to create a copy
|
||
getLastMove(selectedX, selectedY);
|
||
}
|
||
|
||
public void undoLastMove() {
|
||
|
||
if (!previousStates.isEmpty()) {
|
||
Board previousState = previousStates.remove(previousStates.size() - 1); // Get the last state
|
||
this.colNum = previousState.colNum;
|
||
this.lineNum = previousState.lineNum;
|
||
this.turnNumber = previousState.turnNumber;
|
||
this.isWhiteTurn = previousState.isWhiteTurn;
|
||
this.pieces = new ArrayList<>(previousState.pieces); // Restore pieces
|
||
// Reset selected positions and highlighted squares
|
||
this.selectedX = previousState.selectedX;
|
||
this.selectedY = previousState.selectedY;
|
||
this.highlightedSquares = new ArrayList<>(previousState.highlightedSquares);
|
||
} else {
|
||
System.out.println("There are no moves to undo.");
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
public Board(Board board) {
|
||
this.colNum = board.colNum;
|
||
this.lineNum = board.lineNum;
|
||
this.turnNumber = board.turnNumber;
|
||
this.isWhiteTurn = board.isWhiteTurn;
|
||
|
||
this.pieces = new ArrayList<>();
|
||
for (int i = 0; i < board.pieces.size(); i++) {
|
||
Piece original = board.pieces.get(i);
|
||
Piece copy = new Piece(original.getX(), original.getY(), original.isWhite(), original.getType());
|
||
this.pieces.add(copy);
|
||
}
|
||
|
||
this.selectedX = -1;
|
||
this.selectedY = -1;
|
||
this.highlightedSquares = new ArrayList<>();
|
||
}
|
||
|
||
public void playMove(Move move) {
|
||
if (move == null) return;
|
||
|
||
Piece piece = move.getPiece();
|
||
int toX = move.getToX();
|
||
int toY = move.getToY();
|
||
|
||
// Remove captured piece if any
|
||
Piece captured = move.getCaptured();
|
||
if (captured != null) {
|
||
pieces.remove(captured);
|
||
}
|
||
|
||
// Move the piece
|
||
piece.setX(toX);
|
||
piece.setY(toY);
|
||
|
||
// Switch turn
|
||
isWhiteTurn = !isWhiteTurn;
|
||
turnNumber++;
|
||
|
||
// Clear selection and highlights (if needed in GUI)
|
||
selectedX = -1;
|
||
selectedY = -1;
|
||
highlightedSquares.clear();
|
||
|
||
// Check status after move
|
||
for (int i = 0; i < 2; i++) {
|
||
boolean white = (i == 0);
|
||
if (isKingInCheck(white)) {
|
||
System.out.println((white ? "White" : "Black") + " is in check!");
|
||
if (isCheckmate(white)) {
|
||
System.out.println((white ? "White" : "Black") + " is in checkmate!");
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
public ArrayList<int[]> getValidMoves(Piece piece) {
|
||
MoveConditions moveHelper = new MoveConditions(piece, this);
|
||
|
||
switch (piece.getType()) {
|
||
case Pawn:
|
||
return moveHelper.getPawnMoves();
|
||
case Knight:
|
||
return moveHelper.getKnightMoves();
|
||
case Rook:
|
||
return moveHelper.getRookMoves();
|
||
case Bishop:
|
||
return moveHelper.getBishopMoves();
|
||
case Queen:
|
||
return moveHelper.getQueenMoves();
|
||
case King:
|
||
return moveHelper.getKingMoves();
|
||
default:
|
||
return new ArrayList<>();
|
||
}
|
||
}
|
||
|
||
public boolean isKingInCheck(boolean whiteKing) {
|
||
Piece king = null;
|
||
|
||
for (int i = 0; i < pieces.size(); i++) {
|
||
Piece p = pieces.get(i);
|
||
if (p.getType() == PieceType.King && p.isWhite() == whiteKing) {
|
||
king = p;
|
||
}
|
||
}
|
||
|
||
boolean inCheck = false;
|
||
|
||
if (king != null) {
|
||
int kingX = king.getX();
|
||
int kingY = king.getY();
|
||
|
||
for (int i = 0; i < pieces.size(); i++) {
|
||
Piece p = pieces.get(i);
|
||
if (p.isWhite() != whiteKing && p.getType() != PieceType.King) {
|
||
ArrayList<int[]> moves = getValidMoves(p);
|
||
|
||
for (int j = 0; j < moves.size(); j++) {
|
||
int[] move = moves.get(j);
|
||
if (move[0] == kingX && move[1] == kingY) {
|
||
inCheck = true;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return inCheck;
|
||
}
|
||
|
||
public boolean isCheckmate(boolean whiteKing) {
|
||
boolean kingInCheck = isKingInCheck(whiteKing);
|
||
boolean hasEscape = false;
|
||
|
||
// 1. If the king is not in check, it's never checkmate
|
||
if (!kingInCheck) {
|
||
return false;
|
||
}
|
||
|
||
// 2. Try every possible move of every piece belonging to the checked side
|
||
for (int i = 0; i < pieces.size(); i++) {
|
||
Piece piece = pieces.get(i);
|
||
|
||
if (piece.isWhite() == whiteKing) {
|
||
ArrayList<int[]> rawMoves = getValidMoves(piece);
|
||
|
||
for (int j = 0; j < rawMoves.size(); j++) {
|
||
int[] move = rawMoves.get(j);
|
||
int newX = move[0];
|
||
int newY = move[1];
|
||
|
||
// 3. Simulate this move on a copied board
|
||
Board simBoard = new Board(this);
|
||
|
||
// 4. Find the corresponding piece on the cloned board
|
||
Piece simPiece = null;
|
||
for (int k = 0; k < simBoard.getPieces().size(); k++) {
|
||
Piece p = simBoard.getPieces().get(k);
|
||
if (p.getX() == piece.getX() && p.getY() == piece.getY()
|
||
&& p.getType() == piece.getType()
|
||
&& p.isWhite() == piece.isWhite()) {
|
||
simPiece = p;
|
||
}
|
||
}
|
||
|
||
// 5. Apply the move and check if king is still in check
|
||
if (simPiece != null) {
|
||
Piece captured = simBoard.getPieceAt(newX, newY);
|
||
if (captured != null) {
|
||
simBoard.getPieces().remove(captured);
|
||
}
|
||
|
||
simPiece.setX(newX);
|
||
simPiece.setY(newY);
|
||
|
||
if (!simBoard.isKingInCheck(whiteKing)) {
|
||
hasEscape = true;
|
||
System.out.println("ESCAPE FOUND: " + piece.getType() + " from (" + piece.getX() + "," + piece.getY() + ") to (" + newX + "," + newY + ")");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 6. If the king is in check and no move avoids it → checkmate
|
||
return kingInCheck && !hasEscape;
|
||
}
|
||
|
||
}
|