Actualiser src/backend/Board.java

This commit is contained in:
Guillaume VALLENET 2025-04-25 16:08:31 +02:00
parent edcf16e6e3
commit df366fa41e
1 changed files with 133 additions and 104 deletions

View File

@ -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<Position> 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<Piece> getPieces() {
ArrayList<Piece> 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;