OOP_2A5_Project/src/backend/Board.java

517 lines
16 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 players 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;
}
}