From df366fa41e6b61c624a6390a764b37bb809a9855 Mon Sep 17 00:00:00 2001 From: Guillaume VALLENET Date: Fri, 25 Apr 2025 16:08:31 +0200 Subject: [PATCH] Actualiser src/backend/Board.java --- src/backend/Board.java | 237 +++++++++++++++++++++++------------------ 1 file changed, 133 insertions(+), 104 deletions(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index 9b876b0..e7ae55b 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -4,30 +4,35 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.Set; +/** + * Board class represents a chess board with pieces and game logic + */ public class Board { - private int width; - private int height; - private Piece[][] pieces; // 2D array to represent the chess board - private int turnNumber; - private boolean isWhiteTurn; - private int selectedX; - private int selectedY; - private boolean hasSelection; + private int width; // Width of the chess board + private int height; // Height of the chess board + private Piece[][] pieces; // 2D array to represent the chess board + private int turnNumber; // Current turn number in the game + private boolean isWhiteTurn; // True if it's white's turn, false if black's turn + private int selectedX; // X-coordinate of selected piece + private int selectedY; // Y-coordinate of selected piece + private boolean hasSelection; // True if a piece is currently selected private Set validMoves; // Set of valid positions for the selected piece - private MoveHistory moveHistory; // Added move history + private MoveHistory moveHistory; // Records game moves for undo functionality /** * Position class to store x,y coordinates and enable using them in HashSet */ private static class Position { - private final int x; - private final int y; + private final int x; // X-coordinate + private final int y; // Y-coordinate + // Constructor for Position public Position(int x, int y) { this.x = x; this.y = y; } + // Check if two positions are equal @Override public boolean equals(Object obj) { if (this == obj) return true; @@ -36,6 +41,7 @@ public class Board { return x == position.x && y == position.y; } + // Generate hash code for use in HashSet @Override public int hashCode() { return 31 * x + y; @@ -50,14 +56,14 @@ public class Board { public Board(int width, int height) { this.width = width; this.height = height; - this.pieces = new Piece[width][height]; - this.turnNumber = 0; - this.isWhiteTurn = true; // White goes first in chess - this.hasSelection = false; - this.selectedX = -1; + this.pieces = new Piece[width][height]; // Create empty board + this.turnNumber = 0; // Start at turn 0 + this.isWhiteTurn = true; // White goes first in chess + this.hasSelection = false; // No piece is selected initially + this.selectedX = -1; // Invalid selection coordinates this.selectedY = -1; - this.validMoves = new HashSet<>(); - this.moveHistory = new MoveHistory(); // Initialize move history + this.validMoves = new HashSet<>(); // Empty set of valid moves + this.moveHistory = new MoveHistory(); // Initialize move history } /** @@ -96,6 +102,7 @@ public class Board { * @param y Y-coordinate */ public void setPiece(boolean isWhite, PieceType type, int x, int y) { + // Check if coordinates are valid if (x >= 0 && x < width && y >= 0 && y < height) { pieces[x][y] = new Piece(x, y, type, isWhite); } @@ -105,60 +112,63 @@ public class Board { * Sets up the board with the standard chess starting position */ public void populateBoard() { - // Place pawns + // Place pawns on the second and seventh rows for (int x = 0; x < width; x++) { - setPiece(true, PieceType.Pawn, x, 6); // White pawns - setPiece(false, PieceType.Pawn, x, 1); // Black pawns + setPiece(true, PieceType.Pawn, x, 6); // White pawns on row 6 + setPiece(false, PieceType.Pawn, x, 1); // Black pawns on row 1 } - // Place Rooks - setPiece(true, PieceType.Rook, 0, 7); - setPiece(true, PieceType.Rook, 7, 7); - setPiece(false, PieceType.Rook, 0, 0); - setPiece(false, PieceType.Rook, 7, 0); + // Place Rooks in the corners + setPiece(true, PieceType.Rook, 0, 7); // White rook on a1 + setPiece(true, PieceType.Rook, 7, 7); // White rook on h1 + setPiece(false, PieceType.Rook, 0, 0); // Black rook on a8 + setPiece(false, PieceType.Rook, 7, 0); // Black rook on h8 - // Place Knights - setPiece(true, PieceType.Knight, 1, 7); - setPiece(true, PieceType.Knight, 6, 7); - setPiece(false, PieceType.Knight, 1, 0); - setPiece(false, PieceType.Knight, 6, 0); + // Place Knights next to Rooks + setPiece(true, PieceType.Knight, 1, 7); // White knight on b1 + setPiece(true, PieceType.Knight, 6, 7); // White knight on g1 + setPiece(false, PieceType.Knight, 1, 0); // Black knight on b8 + setPiece(false, PieceType.Knight, 6, 0); // Black knight on g8 - // Place Bishops - setPiece(true, PieceType.Bishop, 2, 7); - setPiece(true, PieceType.Bishop, 5, 7); - setPiece(false, PieceType.Bishop, 2, 0); - setPiece(false, PieceType.Bishop, 5, 0); + // Place Bishops next to Knights + setPiece(true, PieceType.Bishop, 2, 7); // White bishop on c1 + setPiece(true, PieceType.Bishop, 5, 7); // White bishop on f1 + setPiece(false, PieceType.Bishop, 2, 0); // Black bishop on c8 + setPiece(false, PieceType.Bishop, 5, 0); // Black bishop on f8 - // Place Queens - setPiece(true, PieceType.Queen, 3, 7); - setPiece(false, PieceType.Queen, 3, 0); + // Place Queens on their color squares + setPiece(true, PieceType.Queen, 3, 7); // White queen on d1 + setPiece(false, PieceType.Queen, 3, 0); // Black queen on d8 - // Place Kings - setPiece(true, PieceType.King, 4, 7); - setPiece(false, PieceType.King, 4, 0); + // Place Kings next to Queens + setPiece(true, PieceType.King, 4, 7); // White king on e1 + setPiece(false, PieceType.King, 4, 0); // Black king on e8 - // Reset turn information + // Reset game state turnNumber = 0; isWhiteTurn = true; - // Clear selection and valid moves + // Clear selection and move history clearSelection(); - moveHistory.clear(); // Clear move history when starting a new game + moveHistory.clear(); } /** * Removes all pieces from the board */ public void cleanBoard() { + // Set all squares to null (no pieces) for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { pieces[x][y] = null; } } + + // Reset game state turnNumber = 0; isWhiteTurn = true; clearSelection(); - moveHistory.clear(); // Clear move history when cleaning the board + moveHistory.clear(); } /** @@ -166,14 +176,18 @@ public class Board { */ public String toString() { StringBuilder sb = new StringBuilder(); + // Display turn information sb.append("Turn: ").append(turnNumber).append(", ").append(isWhiteTurn ? "White" : "Black").append("\n"); + // Print the board with row numbers for (int y = 0; y < height; y++) { - sb.append(8 - y).append(" "); + sb.append(8 - y).append(" "); // Row numbers from 8 to 1 for (int x = 0; x < width; x++) { if (pieces[x][y] == null) { + // Empty square, use dots or spaces for checkerboard pattern sb.append(((x + y) % 2 == 0) ? "." : " "); } else { + // Show piece with uppercase for white, lowercase for black Piece p = pieces[x][y]; char c = p.getType().getSummary().charAt(0); sb.append(p.isWhite() ? Character.toUpperCase(c) : Character.toLowerCase(c)); @@ -182,6 +196,7 @@ public class Board { } sb.append("\n"); } + // Print column letters at bottom sb.append(" a b c d e f g h"); return sb.toString(); } @@ -191,6 +206,7 @@ public class Board { */ public ArrayList getPieces() { ArrayList piecesList = new ArrayList<>(); + // Collect all non-null pieces on the board for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { if (pieces[x][y] != null) { @@ -207,56 +223,57 @@ public class Board { * @param y Y-coordinate */ public void userTouch(int x, int y) { + // Ignore clicks outside the board if (x < 0 || x >= width || y < 0 || y >= height) { - return; // Out of bounds + return; } if (!hasSelection) { - // If no piece is selected yet, select the piece at the clicked position + // Case 1: No piece selected yet if (pieces[x][y] != null && pieces[x][y].isWhite() == isWhiteTurn) { - // Select the piece and calculate valid moves + // Select piece if it's the current player's color selectedX = x; selectedY = y; hasSelection = true; - calculateValidMoves(); + calculateValidMoves(); // Calculate where this piece can move } } else { - // If a piece is already selected + // Case 2: A piece is already selected if (selectedX == x && selectedY == y) { - // If the same position is clicked again, unselect it + // Click on same piece again = deselect clearSelection(); } else if (isHighlighted(x, y)) { - // Move the selected piece to the new position if it's a valid move + // Click on a valid move destination = move the piece Piece selectedPiece = pieces[selectedX][selectedY]; - Piece capturedPiece = pieces[x][y]; // Store captured piece (null if no capture) + Piece capturedPiece = pieces[x][y]; // May be null if no capture // Create a Move object to record this move Move move = new Move(selectedX, selectedY, x, y, selectedPiece, capturedPiece); - // Move the piece (captures any piece at destination) + // Execute move (capture happens implicitly by overwriting) pieces[x][y] = selectedPiece; pieces[selectedX][selectedY] = null; - // Update the piece's position + // Update piece's internal position selectedPiece.setPosition(x, y); - // Record the move in history + // Add move to history moveHistory.recordMove(move); - // Update turn information + // Switch to next player's turn turnNumber++; isWhiteTurn = !isWhiteTurn; - // Reset selection and valid moves + // Clear selection after move clearSelection(); } else if (pieces[x][y] != null && pieces[x][y].isWhite() == isWhiteTurn) { - // If clicked on another piece of the same color, select it instead + // Click on another friendly piece = switch selection selectedX = x; selectedY = y; hasSelection = true; calculateValidMoves(); } else { - // Clicked on an invalid position, unselect + // Click on empty square or enemy piece that's not a valid move = cancel selection clearSelection(); } } @@ -269,7 +286,7 @@ public class Board { hasSelection = false; selectedX = -1; selectedY = -1; - validMoves.clear(); + validMoves.clear(); // Clear highlighted squares } /** @@ -283,6 +300,7 @@ public class Board { Piece piece = pieces[selectedX][selectedY]; if (piece == null) return; + // Delegate to specific method based on piece type switch (piece.getType()) { case Pawn: calculatePawnMoves(piece); @@ -314,25 +332,27 @@ public class Board { int y = piece.getY(); boolean isWhite = piece.isWhite(); - // Direction is -1 for white (moving up) and 1 for black (moving down) + // Direction depends on piece color (white moves up, black moves down) int direction = isWhite ? -1 : 1; int startingRow = isWhite ? 6 : 1; - // Move forward one square + // Check forward one square if (isInsideBoard(x, y + direction) && pieces[x][y + direction] == null) { + // Forward one square is valid if empty validMoves.add(new Position(x, y + direction)); - // Move forward two squares from starting position + // Check forward two squares from starting position if (y == startingRow && isInsideBoard(x, y + 2 * direction) && pieces[x][y + 2 * direction] == null) { validMoves.add(new Position(x, y + 2 * direction)); } } - // Capture diagonally - for (int dx = -1; dx <= 1; dx += 2) { + // Check diagonal captures + for (int dx = -1; dx <= 1; dx += 2) { // Check both left and right diagonal int newX = x + dx; int newY = y + direction; + // Can capture diagonally if opponent's piece is there if (isInsideBoard(newX, newY) && pieces[newX][newY] != null && pieces[newX][newY].isWhite() != isWhite) { validMoves.add(new Position(newX, newY)); } @@ -348,29 +368,31 @@ public class Board { int y = piece.getY(); boolean isWhite = piece.isWhite(); - // Four directions: horizontal and vertical - int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; + // Rook moves in four straight directions + int[][] directions = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // Up, right, down, left + // Check all four directions for (int[] dir : directions) { int dx = dir[0]; int dy = dir[1]; + // Continue in this direction until hitting a piece or board edge for (int step = 1; step < Math.max(width, height); step++) { int newX = x + dx * step; int newY = y + dy * step; - if (!isInsideBoard(newX, newY)) break; // Out of bounds + if (!isInsideBoard(newX, newY)) break; // Hit board edge if (pieces[newX][newY] == null) { - // Empty square, can move here + // Empty square is valid move validMoves.add(new Position(newX, newY)); } else { - // Square has a piece + // Hit a piece if (pieces[newX][newY].isWhite() != isWhite) { // Can capture opponent's piece validMoves.add(new Position(newX, newY)); } - break; // Can't move past a piece + break; // Can't move past any piece } } } @@ -385,16 +407,18 @@ public class Board { int y = piece.getY(); boolean isWhite = piece.isWhite(); - // All possible knight moves + // Knight has 8 possible L-shaped moves int[][] moves = { {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -1}, {2, 1} }; + // Check all potential knight moves for (int[] move : moves) { int newX = x + move[0]; int newY = y + move[1]; + // Knight can move to empty square or capture opponent's piece if (isInsideBoard(newX, newY) && (pieces[newX][newY] == null || pieces[newX][newY].isWhite() != isWhite)) { validMoves.add(new Position(newX, newY)); } @@ -410,29 +434,31 @@ public class Board { int y = piece.getY(); boolean isWhite = piece.isWhite(); - // Four diagonal directions - int[][] directions = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; + // Bishop moves diagonally in four directions + int[][] directions = {{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; // diagonal directions + // Check all diagonal directions for (int[] dir : directions) { int dx = dir[0]; int dy = dir[1]; + // Continue in this direction until hitting a piece or board edge for (int step = 1; step < Math.max(width, height); step++) { int newX = x + dx * step; int newY = y + dy * step; - if (!isInsideBoard(newX, newY)) break; // Out of bounds + if (!isInsideBoard(newX, newY)) break; // Hit board edge if (pieces[newX][newY] == null) { - // Empty square, can move here + // Empty square is valid move validMoves.add(new Position(newX, newY)); } else { - // Square has a piece + // Hit a piece if (pieces[newX][newY].isWhite() != isWhite) { // Can capture opponent's piece validMoves.add(new Position(newX, newY)); } - break; // Can't move past a piece + break; // Can't move past any piece } } } @@ -443,9 +469,9 @@ public class Board { * @param piece The queen piece */ private void calculateQueenMoves(Piece piece) { - // Queen combines rook and bishop movement - calculateRookMoves(piece); - calculateBishopMoves(piece); + // Queen moves like both a rook and bishop combined + calculateRookMoves(piece); // Get straight moves + calculateBishopMoves(piece); // Add diagonal moves } /** @@ -457,14 +483,15 @@ public class Board { int y = piece.getY(); boolean isWhite = piece.isWhite(); - // All 8 directions around the king + // King can move one square in any direction for (int dx = -1; dx <= 1; dx++) { for (int dy = -1; dy <= 1; dy++) { - if (dx == 0 && dy == 0) continue; // Skip the current position + if (dx == 0 && dy == 0) continue; // Skip current position int newX = x + dx; int newY = y + dy; + // King can move to empty square or capture opponent's piece if (isInsideBoard(newX, newY) && (pieces[newX][newY] == null || pieces[newX][newY].isWhite() != isWhite)) { validMoves.add(new Position(newX, newY)); } @@ -510,22 +537,22 @@ public class Board { return; // No moves to undo } - // Get the last move + // Get the last move from history Move lastMove = moveHistory.getLastMove(); - // Get the moving piece (which is now at the destination) + // Get the piece that moved (now at destination) Piece movingPiece = pieces[lastMove.getToX()][lastMove.getToY()]; if (movingPiece == null) { - // Something went wrong, the piece isn't where it should be + // Something went wrong, the piece should be at destination return; } - // Move the piece back to its original position + // Move the piece back to original position pieces[lastMove.getFromX()][lastMove.getFromY()] = movingPiece; movingPiece.setPosition(lastMove.getFromX(), lastMove.getFromY()); - // If a piece was captured, put it back + // If a piece was captured, restore it if (lastMove.getCapturedPiece() != null) { Piece capturedPiece = lastMove.getCapturedPiece(); pieces[lastMove.getToX()][lastMove.getToY()] = capturedPiece; @@ -534,11 +561,11 @@ public class Board { pieces[lastMove.getToX()][lastMove.getToY()] = null; } - // Revert turn information + // Revert to previous turn turnNumber--; isWhiteTurn = !isWhiteTurn; - // Clear any selection and highlighting + // Clear any selection clearSelection(); } @@ -547,8 +574,8 @@ public class Board { * @param array Array of strings representing the board */ public Board(String[] array) { - this(8, 8); // Default to standard 8x8 board - // The rest will be implemented in part 3 + this(8, 8); // Start with standard 8x8 board + // File loading will be implemented in part 3 } /** @@ -558,10 +585,11 @@ public class Board { public Board(Board board) { this(board.getWidth(), board.getHeight()); + // Copy board state this.turnNumber = board.getTurnNumber(); this.isWhiteTurn = board.isTurnWhite(); - // Copy pieces + // Copy all pieces from original board for (Piece piece : board.getPieces()) { this.setPiece(piece.isWhite(), piece.getType(), piece.getX(), piece.getY()); } @@ -572,7 +600,7 @@ public class Board { * @return Array of strings representing the board */ public String[] toFileRep() { - // This will be implemented in part 3 + // Will be implemented in part 3 return null; } @@ -585,33 +613,34 @@ public class Board { return; } + // Get move coordinates int fromX = move.getFromX(); int fromY = move.getFromY(); int toX = move.getToX(); int toY = move.getToY(); - // Make sure there's a piece at the starting position + // Verify there's a piece to move if (pieces[fromX][fromY] == null) { return; } - // Store the piece that might be captured + // Get the piece that might be captured Piece capturedPiece = pieces[toX][toY]; - // Update the move object with the actual pieces + // Create a move with actual pieces for history move = new Move(fromX, fromY, toX, toY, pieces[fromX][fromY], capturedPiece); - // Make the move + // Execute the move pieces[toX][toY] = pieces[fromX][fromY]; pieces[fromX][fromY] = null; - // Update the piece's position + // Update piece's internal position pieces[toX][toY].setPosition(toX, toY); - // Record the move + // Record move in history moveHistory.recordMove(move); - // Update turn information + // Advance to next turn turnNumber++; isWhiteTurn = !isWhiteTurn;