package backend; import java.util.ArrayList; public class Board { private int colNum; private int lineNum; private int turnNumber; private boolean isWhiteTurn; ArrayList pieces = new ArrayList<>(); private int selectedX = -1; private int selectedY = -1; ArrayList highlightedSquares = new ArrayList<>(); private boolean pawnDoubleStep; private int xCoordinatePawn; private int yCoordinatePawn; private ArrayList 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 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 the current state before making a move getLastMove(); //store for castling int originalX = selectedPiece.getX(); // 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 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; } //new method to save the current state of the board public void getLastMove() { previousStates.add(new Board(this)); // Create a copy of the current board state } 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 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 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 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; } }