package backend; import java.util.ArrayList; public class Board { private int width; private int height; private ArrayList pieces; private int turnNumber; private boolean turnWhite; private Integer selectedX = null; private Integer selectedY = null; private ArrayList highlightedSquares = new ArrayList<>(); private ArrayList moveHistory = new ArrayList<>(); private int[] enPassantTarget = null; /*public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; this.pieces = new ArrayList<>(); } */ public Board(int colNum, int lineNum) { this.width = colNum; this.height = lineNum; this.pieces = new ArrayList<>(); this.turnNumber = 0; this.turnWhite = true; // White starts first in chess } public int getWidth() { return width; } public int getHeight() { return height;// the above part set the checked board } public int getTurnNumber() { return this.turnNumber; } public boolean isTurnWhite() { return this.turnWhite; } public void setPiece(boolean isWhite, PieceType type, int x, int y) { pieces.add(new Piece(isWhite, type, x, y)); } public void populateBoard() { cleanBoard(); // make sure it's empty first // Place white pawns for (int x = 0; x < 8; x++) { setPiece(false, PieceType.Pawn, x, 1); } // Place black pawns for (int x = 0; x < 8; x++) { setPiece(true, PieceType.Pawn, x, 6); } // Back rows (R, N, B, K, Q, B, N, R) PieceType[] backRow = { PieceType.Rook, PieceType.Knight, PieceType.Bishop, PieceType.Queen, PieceType.King, PieceType.Bishop, PieceType.Knight, PieceType.Rook }; // White back row for (int x = 0; x < 8; x++) { setPiece(false, backRow[x], x, 0); } // Black back row for (int x = 0; x < 8; x++) { setPiece(true, backRow[x], x, 7); } } public void cleanBoard() { //TODO pieces.clear(); } @Override public String toString() { String[][] grid = new String[height][width]; // Fill the grid with empty squares for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { grid[y][x] = "--"; } } // Place pieces on the grid for (Piece piece : pieces) { String color = piece.isWhite() ? "W" : "B"; String type = piece.getType().toString().substring(0, 1); // e.g., "P" for Pawn grid[piece.getY()][piece.getX()] = color + type; } // Build the string row by row StringBuilder sb = new StringBuilder(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { sb.append(grid[y][x]).append(" "); } sb.append("\n"); } return sb.toString(); } public ArrayList getPieces() { return pieces; // this refers to the instance variable } public void userTouch(int x, int y) { Piece clickedPiece = getPieceAt(x, y); // No selection yet if (selectedX == null || selectedY == null) { if (clickedPiece != null && clickedPiece.isWhite() == turnWhite) { selectedX = x; selectedY = y; highlightedSquares = computeLegalMoves(clickedPiece); } return; } // Clicked the same square again → unselect if (selectedX == x && selectedY == y) { selectedX = null; selectedY = null; highlightedSquares.clear(); return; } // Otherwise, try to move Piece selectedPiece = getPieceAt(selectedX, selectedY); if (selectedPiece == null) { // Somehow no piece at selected position (safety check) selectedX = null; selectedY = null; highlightedSquares.clear(); return; } // Check if clicked destination is valid boolean validMove = false; for (int[] pos : highlightedSquares) { if (pos[0] == x && pos[1] == y) { validMove = true; break; } } if (!validMove) { // Invalid destination → reset selectedX = null; selectedY = null; highlightedSquares.clear(); return; } // Move is valid → capture if needed Piece target = getPieceAt(x, y); // Log the move for undo Move move = new Move( selectedPiece.getType(), selectedPiece.isWhite(), selectedX, selectedY, x, y, target // may be null ); moveHistory.add(move); // Remove captured piece if any if (target != null) { pieces.remove(target); } // Remove the original piece pieces.remove(selectedPiece); // Add new piece at destination setPiece(selectedPiece.isWhite(), selectedPiece.getType(), x, y); // Update turn turnNumber++; turnWhite = !turnWhite; // Clear selection and highlights selectedX = null; selectedY = null; highlightedSquares.clear(); // Check for en passant capture BEFORE updating enPassantTarget! if (selectedPiece.getType() == PieceType.Pawn && enPassantTarget != null && x == enPassantTarget[0] && y == enPassantTarget[1]) { int capturedY = selectedPiece.isWhite() ? y - 1 : y + 1; Piece capturedPawn = getPieceAt(x, capturedY); // Validate that it's an enemy pawn that just moved two steps if (capturedPawn != null && capturedPawn.getType() == PieceType.Pawn && capturedPawn.isWhite() != selectedPiece.isWhite()) { // Remove the pawn captured en passant pieces.remove(capturedPawn); } } // Update enPassantTarget if (selectedPiece.getType() == PieceType.Pawn && selectedY != null && Math.abs(y - selectedY) == 2) { int middleY = (y + selectedY) / 2; enPassantTarget = new int[]{x, middleY}; } else { enPassantTarget = null; } } public boolean isSelected(int x, int y) { return selectedX != null && selectedY != null && selectedX == x && selectedY == y; } /* saving-loading feature :*/ public String[] toFileRep() { String[] fileRep = new String[height + 1]; // 8 rows + 1 for turn String[][] grid = new String[height][width]; // Fill with empty squares for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { grid[y][x] = "--"; } } // Fill with pieces for (Piece piece : pieces) { String color = piece.isWhite() ? "W" : "B"; String type = piece.getType().toString().substring(0, 1); // P, R, N, B, Q, K grid[piece.getY()][piece.getX()] = color + type; } // Convert grid to CSV-like strings for (int y = 0; y < height; y++) { StringBuilder row = new StringBuilder(); for (int x = 0; x < width; x++) { row.append(grid[y][x]); if (x < width - 1) { row.append(","); } } fileRep[y] = row.toString(); } // Last line = turn fileRep[height] = turnWhite ? "W" : "B"; return fileRep; } public Board(String[] array) { this.width = 8; this.height = 8; this.pieces = new ArrayList<>(); this.turnNumber = 0; this.turnWhite = array[8].equals("W"); for (int y = 0; y < height; y++) { String[] row = array[y].split(","); for (int x = 0; x < width; x++) { String code = row[x]; if (!code.equals("--")) { boolean isWhite = code.charAt(0) == 'W'; char typeChar = code.charAt(1); PieceType type = null; switch (typeChar) { case 'P': type = PieceType.Pawn; break; case 'R': type = PieceType.Rook; break; case 'N': type = PieceType.Knight; break; case 'B': type = PieceType.Bishop; break; case 'Q': type = PieceType.Queen; break; case 'K': type = PieceType.King; break; } if (type != null) { setPiece(isWhite, type, x, y); } } } } } /* 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 undoLastMove() { if (moveHistory.isEmpty()) return; Move lastMove = moveHistory.remove(moveHistory.size() - 1); // Remove piece from destination Piece movedPiece = getPieceAt(lastMove.getToX(), lastMove.getToY()); if (movedPiece != null) { pieces.remove(movedPiece); } // Restore moved piece to original location setPiece(lastMove.isWhite(), lastMove.getType(), lastMove.getFromX(), lastMove.getFromY()); // Restore captured piece if there was one if (lastMove.getCapturedPiece() != null) { Piece cap = lastMove.getCapturedPiece(); setPiece(cap.isWhite(), cap.getType(), cap.getX(), cap.getY()); } turnNumber--; turnWhite = !turnWhite; } public Board(Board board) { this.width = board.getWidth(); this.height = board.getHeight(); this.turnNumber = board.getTurnNumber(); this.turnWhite = board.isTurnWhite(); this.pieces = new ArrayList<>(); for (Piece p : board.getPieces()) { // Create a new Piece with the same values (deep copy) Piece copy = new Piece(p.isWhite(), p.getType(), p.getX(), p.getY()); this.pieces.add(copy); } // Optional: copy selected square if needed this.selectedX = board.selectedX; this.selectedY = board.selectedY; // Optional: copy highlighted squares this.highlightedSquares = new ArrayList<>(); for (int[] square : board.highlightedSquares) { this.highlightedSquares.add(new int[]{square[0], square[1]}); } // Optional: copy move history (use only if Move has a proper copy strategy) this.moveHistory = new ArrayList<>(board.moveHistory); } public void playMove(Move move) { // Get the piece to move Piece pieceToMove = getPieceAt(move.getFromX(), move.getFromY()); if (pieceToMove == null) return; // Invalid move, nothing to move // Capture if there's a piece at the destination Piece captured = getPieceAt(move.getToX(), move.getToY()); if (captured != null) { pieces.remove(captured); } // Remove the original piece pieces.remove(pieceToMove); // Add the moved piece at new location setPiece(move.isWhite(), move.getType(), move.getToX(), move.getToY()); // Save move to history moveHistory.add(move); // Update turn turnNumber++; turnWhite = !turnWhite; // Clear selection and highlights (optional safety) selectedX = null; selectedY = null; highlightedSquares.clear(); } private Piece getPieceAt(int x, int y) { for (Piece p : pieces) { if (p.getX() == x && p.getY() == y) { return p; } } return null; } private boolean isEmpty(int x, int y) { return getPieceAt(x, y) == null; } private boolean isEnemy(int x, int y, boolean isWhite) { Piece p = getPieceAt(x, y); return p != null && p.isWhite() != isWhite; } public ArrayList computeLegalMoves(Piece piece) { ArrayList moves = new ArrayList<>(); int x = piece.getX(); int y = piece.getY(); PieceType type = piece.getType(); if (type == PieceType.Pawn) { int dir = piece.isWhite() ? -1 : 1; int nextY = y + dir; // Move forward if square is empty if (isEmpty(x, nextY)) { moves.add(new int[]{x, nextY}); // First move double step int startRow = piece.isWhite() ? 6 : 1; int twoStepsY = y + 2 * dir; if (y == startRow && isEmpty(x, twoStepsY)) { moves.add(new int[]{x, twoStepsY}); } if (nextY == 7 ){ setPiece(false , PieceType.Queen, x,nextY); pieces.remove(piece); } if (nextY==0 ){ setPiece(true , PieceType.Queen, x,nextY); pieces.remove(piece); } } // Diagonal capture if (isEnemy(x - 1, nextY, piece.isWhite())) { moves.add(new int[]{x - 1, nextY}); } if (isEnemy(x + 1, nextY, piece.isWhite())) { moves.add(new int[]{x + 1, nextY}); } // En passant if (enPassantTarget != null) { int targetX = enPassantTarget[0]; int targetY = enPassantTarget[1]; if (nextY == targetY && Math.abs(x - targetX) == 1) { moves.add(new int[]{targetX, targetY}); } } // Promotion check (you shouldn't promote here — only on move execution) // Do NOT change piece type inside this method } if (type == PieceType.Rook) { // Directions : haut, bas, gauche, droite int[][] directions = {{1,0},{-1,0},{0,1},{0,-1}}; for (int[] dir : directions) { int nx = x + dir[0]; int ny = y + dir[1]; while (nx >= 0 && nx < width && ny >= 0 && ny < height) { if (isEmpty(nx, ny)) { moves.add(new int[]{nx, ny}); } else { if (isEnemy(nx, ny, piece.isWhite())) { moves.add(new int[]{nx, ny}); } break; } nx += dir[0]; ny += dir[1]; } } } if (type == PieceType.Bishop) { // Directions : diagonales int[][] directions = {{1,1},{1,-1},{-1,1},{-1,-1}}; for (int[] dir : directions) { int nx = x + dir[0]; int ny = y + dir[1]; while (nx >= 0 && nx < width && ny >= 0 && ny < height) { if (isEmpty(nx, ny)) { moves.add(new int[]{nx, ny}); } else { if (isEnemy(nx, ny, piece.isWhite())) { moves.add(new int[]{nx, ny}); } break; } nx += dir[0]; ny += dir[1]; } } } if (type == PieceType.Queen) { // Combine Rook + Bishop int[][] directions = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}}; for (int[] dir : directions) { int nx = x + dir[0]; int ny = y + dir[1]; while (nx >= 0 && nx < width && ny >= 0 && ny < height) { if (isEmpty(nx, ny)) { moves.add(new int[]{nx, ny}); } else { if (isEnemy(nx, ny, piece.isWhite())) { moves.add(new int[]{nx, ny}); } break; } nx += dir[0]; ny += dir[1]; } } } if (type == PieceType.Knight) { // 8 mouvements possibles int[][] jumps = {{2,1},{1,2},{-1,2},{-2,1},{-2,-1},{-1,-2},{1,-2},{2,-1}}; for (int[] jump : jumps) { int nx = x + jump[0]; int ny = y + jump[1]; if (nx >= 0 && nx < width && ny >= 0 && ny < height) { if (isEmpty(nx, ny) || isEnemy(nx, ny, piece.isWhite())) { moves.add(new int[]{nx, ny}); } } } } if (type == PieceType.King) { // 8 directions, une seule case int[][] directions = {{1,0},{-1,0},{0,1},{0,-1},{1,1},{1,-1},{-1,1},{-1,-1}}; for (int[] dir : directions) { int nx = x + dir[0]; int ny = y + dir[1]; if (nx >= 0 && nx < width && ny >= 0 && ny < height) { if (isEmpty(nx, ny) || isEnemy(nx, ny, piece.isWhite())) { moves.add(new int[]{nx, ny}); } } } } return moves; } }