package backend; import java.util.ArrayList; import java.util.Stack; public class Board { private Piece lastDoubleStepPawn = null; private int width; private int height; private ArrayList Pieces; private Stack moveHistory = new Stack<>(); // ── NEW FIELDS FOR userTouch ─────────────────────────────────────── private boolean hasSelection = false; // did we already click to pick up a piece? private int selectedX, selectedY; // if so, which square is “in hand”? private int turnNumber = 0; // how many half-moves have been played? private boolean turnWhite = true; // true = White to play, false = Black // ───────────────────────────────────────────────────────────── public String[] toFileRep() { String[] lines = new String[height + 1]; for (int y = 0; y < height; y++) { StringBuilder sb = new StringBuilder(); for (int x = 0; x < width; x++) { Piece p = getPieceAt(x, y); if (p == null) { sb.append(".."); } else { sb.append(p.isWhite() ? "W" : "B"); sb.append(p.getType().getSummary()); } if (x < width - 1) sb.append(","); } lines[y] = sb.toString(); } lines[height] = isTurnWhite() ? "W" : "B"; return lines; } public Board(String[] array) { this.width = 8; this.height = 8; this.Pieces = new ArrayList<>(); this.moveHistory = new Stack<>(); this.hasSelection = false; this.turnNumber = 0; for (int y = 0; y < 8; y++) { String[] cells = array[y].split(","); for (int x = 0; x < 8; x++) { String cell = cells[x]; if (!cell.equals("..")) { boolean isWhite = cell.charAt(0) == 'W'; PieceType type = PieceType.fromSummary(cell.charAt(1)); Pieces.add(new Piece(x, y, isWhite, type)); } } } this.turnWhite = array[8].equals("W"); } public Board(int width, int height) { this.width = width; this.height = height; this.Pieces = new ArrayList<>(); } public int getWidth() { return width; } public int getHeight() { return height; } public int getTurnNumber() { //TODO return turnNumber; } public boolean isTurnWhite() { //TODO return turnWhite; } public void setPiece(boolean isWhite, PieceType type, int x, int y) { // 1) Remove any piece already at this square for (int i = 0; i < Pieces.size(); i++) { Piece p = Pieces.get(i); if (p.getX() == x && p.getY() == y) { Pieces.remove(i); break; } } // 2) Add the new piece Pieces.add(new Piece(x, y, isWhite, type)); } public void populateBoard() { // black Pieces.add(new Piece(0,0,false,PieceType.Rook)); Pieces.add(new Piece(1,0,false,PieceType.Knight)); Pieces.add(new Piece(2,0,false,PieceType.Bishop)); Pieces.add(new Piece(3,0,false,PieceType.Queen)); Pieces.add(new Piece(4,0,false,PieceType.King)); Pieces.add(new Piece(5,0,false,PieceType.Bishop)); Pieces.add(new Piece(6,0,false,PieceType.Knight)); Pieces.add(new Piece(7,0,false,PieceType.Rook)); Pieces.add(new Piece(0,1,false,PieceType.Pawn)); Pieces.add(new Piece(1,1,false,PieceType.Pawn)); Pieces.add(new Piece(2,1,false,PieceType.Pawn)); Pieces.add(new Piece(3,1,false,PieceType.Pawn)); Pieces.add(new Piece(4,1,false,PieceType.Pawn)); Pieces.add(new Piece(5,1,false,PieceType.Pawn)); Pieces.add(new Piece(6,1,false,PieceType.Pawn)); Pieces.add(new Piece(7,1,false,PieceType.Pawn)); // white Pieces.add(new Piece(0, 7, true, PieceType.Rook)); Pieces.add(new Piece(1, 7, true, PieceType.Knight)); Pieces.add(new Piece(2, 7, true, PieceType.Bishop)); Pieces.add(new Piece(3, 7, true, PieceType.Queen)); Pieces.add(new Piece(4, 7, true, PieceType.King)); Pieces.add(new Piece(5, 7, true, PieceType.Bishop)); Pieces.add(new Piece(6, 7, true, PieceType.Knight)); Pieces.add(new Piece(7, 7, true, PieceType.Rook)); Pieces.add(new Piece(0,6,true,PieceType.Pawn)); Pieces.add(new Piece(1,6,true,PieceType.Pawn)); Pieces.add(new Piece(2,6,true,PieceType.Pawn)); Pieces.add(new Piece(3,6,true,PieceType.Pawn)); Pieces.add(new Piece(4,6,true,PieceType.Pawn)); Pieces.add(new Piece(5,6,true,PieceType.Pawn)); Pieces.add(new Piece(6,6,true,PieceType.Pawn)); Pieces.add(new Piece(7,6,true,PieceType.Pawn)); } public ArrayList getPieces(){ return Pieces; } public void cleanBoard() { Pieces.clear(); } public String toString() { StringBuilder sb = new StringBuilder(); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { Piece found = null; for (Piece p : Pieces) { if (p.getX() == x && p.getY() == y) { found = p; break; } } if (found != null) { String color = found.isWhite() ? "W" : "B"; sb.append(color).append(found.getType().getSummary()).append(" "); } else { sb.append(".. "); } } sb.append("\n"); } return sb.toString(); } public void userTouch(int x, int y) { // 1) Find if you clicked on a piece at (x,y) Piece clicked = null; for (Piece p : Pieces) { if (p.getX() == x && p.getY() == y) { clicked = p; break; } } // 2) If nothing is selected, select a piece of the correct color if (!hasSelection) { if (clicked != null && clicked.isWhite() == turnWhite) { hasSelection = true; selectedX = x; selectedY = y; } return; } // 3) If you click the same square again → deselect if (x == selectedX && y == selectedY) { hasSelection = false; return; } // 4) If the square is not highlighted (illegal), ignore it if (!isHighlighted(x, y)) { return; } // 5) Do the move // Capture (including en passant) Piece captured = null; for (Piece p : Pieces) { if (p.getX() == x && p.getY() == y) { captured = p; break; } } // Get the piece to move (only declare once!) Piece toMove = null; for (Piece p : Pieces) { if (p.getX() == selectedX && p.getY() == selectedY) { toMove = p; break; } } // En passant condition if (captured == null && toMove != null && toMove.getType() == PieceType.Pawn) { int dir = toMove.isWhite() ? -1 : 1; if (lastDoubleStepPawn != null && lastDoubleStepPawn.getX() == x && lastDoubleStepPawn.getY() == y - dir && lastDoubleStepPawn.isWhite() != toMove.isWhite()) { Pieces.removeIf(p -> p.getX() == lastDoubleStepPawn.getX() && p.getY() == lastDoubleStepPawn.getY()); captured = lastDoubleStepPawn; } } // Normal capture if (captured != null) { Pieces.remove(captured); } // Move the selected piece if (toMove != null) { Pieces.removeIf(p -> p.getX() == selectedX && p.getY() == selectedY); // Check for promotion boolean isWhite = toMove.isWhite(); PieceType type = toMove.getType(); if (type == PieceType.Pawn && ((isWhite && y == 0) || (!isWhite && y == 7))) { // Promote to queen Pieces.add(new Piece(x, y, isWhite, PieceType.Queen)); } else { // Normal move Pieces.add(new Piece(x, y, isWhite, type)); } // Track if this move is a double pawn move if (type == PieceType.Pawn && Math.abs(y - toMove.getY()) == 2) { lastDoubleStepPawn = new Piece(x, y, isWhite, PieceType.Pawn); } else { lastDoubleStepPawn = null; } // Update state turnNumber++; turnWhite = !turnWhite; hasSelection = false; Move move = new Move(toMove, x, y, captured); // captured may be null moveHistory.push(move); System.out.println(this); } } public boolean isSelected(int x, int y) { // return true if the exact square is currently picked up return hasSelection && selectedX == x && selectedY == y; // hasSelection is true the moment the user clicks on a piece for the first time } /* The following methods require more work ! */ public boolean isHighlighted(int x, int y) { if (!hasSelection) return false; // Find the selected piece Piece selected = null; for (Piece p : Pieces) { if (p.getX() == selectedX && p.getY() == selectedY) { selected = p; break; } } if (selected == null) return false; int sx = selected.getX(); int sy = selected.getY(); boolean isWhite = selected.isWhite(); // PAWN LOGIC if (selected.getType() == PieceType.Pawn) { int dir = isWhite ? -1 : 1; // One square forward if (x == sx && y == sy + dir) { if (getPieceAt(x, y) == null) return true; } // Two squares from initial rank if ((isWhite && sy == 6) || (!isWhite && sy == 1)) { if (x == sx && y == sy + 2 * dir) { if (getPieceAt(sx, sy + dir) == null && getPieceAt(x, y) == null) { return true; } } } // Capture diagonally if ((x == sx + 1 || x == sx - 1) && y == sy + dir) { Piece target = getPieceAt(x, y); if (target != null && target.isWhite() != isWhite) return true; } // En passant highlight if ((x == sx + 1 || x == sx - 1) && y == sy + dir) { if (lastDoubleStepPawn != null && lastDoubleStepPawn.getX() == x && lastDoubleStepPawn.getY() == sy && lastDoubleStepPawn.isWhite() != isWhite) { return true; } } } // ROOK LOGIC if (selected.getType() == PieceType.Rook) { int[][] directions = { {1,0}, {-1,0}, {0,1}, {0,-1} }; for (int[] d : directions) { int cx = sx + d[0]; int cy = sy + d[1]; while (cx >= 0 && cx < width && cy >= 0 && cy < height) { Piece target = getPieceAt(cx, cy); if (target == null) { if (cx == x && cy == y) return true; } else { if (target.isWhite() != isWhite && cx == x && cy == y) return true; break; } cx += d[0]; cy += d[1]; } } } if (selected.getType() == PieceType.Knight) { int[][] moves = { {1, 2}, {2, 1}, {2, -1}, {1, -2}, {-1, -2}, {-2, -1}, {-2, 1}, {-1, 2} }; for (int[] m : moves) { int nx = sx + m[0]; int ny = sy + m[1]; if (nx == x && ny == y && nx >= 0 && nx < width && ny >= 0 && ny < height) { Piece target = getPieceAt(nx, ny); if (target == null || target.isWhite() != isWhite) { return true; } } } } // BISHOP LOGIC if (selected.getType() == PieceType.Bishop) { int[][] directions = { {1,1}, {-1,1}, {1,-1}, {-1,-1} }; for (int[] d : directions) { int cx = sx + d[0]; int cy = sy + d[1]; while (cx >= 0 && cx < width && cy >= 0 && cy < height) { Piece target = getPieceAt(cx, cy); if (target == null) { if (cx == x && cy == y) return true; } else { if (target.isWhite() != isWhite && cx == x && cy == y) return true; break; // Stop if any piece blocks the diagonal } cx += d[0]; cy += d[1]; } } } // QUEEN LOGIC if (selected.getType() == PieceType.Queen) { int[][] directions = { {1, 0}, {-1, 0}, {0, 1}, {0, -1}, // Rook directions {1, 1}, {-1, 1}, {1, -1}, {-1, -1} // Bishop directions }; for (int[] d : directions) { int cx = sx + d[0]; int cy = sy + d[1]; while (cx >= 0 && cx < width && cy >= 0 && cy < height) { Piece target = getPieceAt(cx, cy); if (target == null) { if (cx == x && cy == y) return true; } else { if (target.isWhite() != isWhite && cx == x && cy == y) return true; break; // Stop if a piece blocks further movement } cx += d[0]; cy += d[1]; } } } // KING LOGIC if (selected.getType() == PieceType.King) { int[][] directions = { {1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, 1}, {1, -1}, {-1, -1} }; for (int[] d : directions) { int nx = sx + d[0]; int ny = sy + d[1]; if (nx == x && ny == y && nx >= 0 && nx < width && ny >= 0 && ny < height) { Piece target = getPieceAt(nx, ny); if (target == null || target.isWhite() != isWhite) { return true; } } } } return false; } public void undoLastMove() { if (moveHistory.isEmpty()) return; // Get the last move from the history Move last = moveHistory.pop(); // Remove the piece from the destination square Pieces.removeIf(p -> p.getX() == last.getToX() && p.getY() == last.getToY()); // Restore the moved piece to its original position Piece moved = last.getMovedPiece(); Pieces.add(new Piece(last.getFromX(), last.getFromY(), moved.isWhite(), moved.getType())); // Restore the captured piece if it existed Piece captured = last.getCapturedPiece(); if (captured != null) { Pieces.add(new Piece(captured.getX(), captured.getY(), captured.isWhite(), captured.getType())); } // Revert turn number and color turnNumber--; turnWhite = !turnWhite; // Clear selection hasSelection = false; } public Board(Board board) { this.width = board.width; this.height = board.height; this.turnNumber = board.turnNumber; this.turnWhite = board.turnWhite; this.hasSelection = board.hasSelection; this.selectedX = board.selectedX; this.selectedY = board.selectedY; // Deep copy of pieces this.Pieces = new ArrayList<>(); for (Piece p : board.Pieces) { this.Pieces.add(new Piece(p.getX(), p.getY(), p.isWhite(), p.getType())); } // Optional: clear move history or leave empty to avoid shared reference this.moveHistory = new Stack<>(); } public void playMove(Move move) { // Remove the piece from the destination (if a capture) Pieces.removeIf(p -> p.getX() == move.getToX() && p.getY() == move.getToY()); // Remove the moved piece from its old position Pieces.removeIf(p -> p.getX() == move.getFromX() && p.getY() == move.getFromY()); // Add the moved piece to the new position Piece moved = move.getMovedPiece(); boolean isWhite = moved.isWhite(); int toY = move.getToY(); PieceType type = moved.getType(); // Check for promotion if (type == PieceType.Pawn && ((isWhite && toY == 0) || (!isWhite && toY == 7))) { Pieces.add(new Piece(move.getToX(), toY, isWhite, PieceType.Queen)); } else { Pieces.add(new Piece(move.getToX(), toY, isWhite, type)); } // Save the move to history moveHistory.push(move); // Update turn turnNumber++; turnWhite = !turnWhite; // Clear selection hasSelection = false; } Piece getPieceAt(int x, int y) { for (Piece p : Pieces) { if (p.getX() == x && p.getY() == y) return p; } return null; } public void selectPiece(int x, int y) { hasSelection = true; selectedX = x; selectedY = y; } }