From cf06917fb50ddc7581d21c2ae32f74a4cebc974e Mon Sep 17 00:00:00 2001 From: apoll Date: Wed, 16 Apr 2025 13:27:02 +0200 Subject: [PATCH] ajout fonction en passant --- src/backend/Board.java | 423 ++++++++++++++++++++++++++--------------- src/backend/Game.java | 411 ++++++++++++++++++++++++++++++--------- src/backend/Move.java | 343 +++++++++++++++------------------ src/backend/Piece.java | 160 ++++++---------- 4 files changed, 794 insertions(+), 543 deletions(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index 79d5b5c..aea0cb3 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -1,18 +1,29 @@ package backend; import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.Objects; // Importer pour Objects.hash si besoin public class Board { - private int width; - private int height; - private ArrayList pieces; + private final int width; + private final int height; + + private final List pieces; private int selectedX; private int selectedY; private int turnNumber; private boolean isTurnWhite; + private Set highlightedPositions; + private int enPassantTargetX; // Nouvelle variable d'état + private int enPassantTargetY; // Nouvelle variable d'état public Board(int colNum, int lineNum) { + if (colNum <= 0 || lineNum <= 0) { + throw new IllegalArgumentException("Board dimensions must be positive."); + } this.width = colNum; this.height = lineNum; this.pieces = new ArrayList<>(); @@ -20,53 +31,105 @@ public class Board { this.selectedY = -1; this.turnNumber = 0; this.isTurnWhite = true; - Move.clearHighlightedPositions(); // Ensure no highlights at start + this.highlightedPositions = new HashSet<>(); + this.enPassantTargetX = -1; // Initialisation + this.enPassantTargetY = -1; // Initialisation } - public int getWidth() { - return this.width; + public Board(Board other) { + this.width = other.width; + this.height = other.height; + this.pieces = new ArrayList<>(other.pieces.size()); + for (Piece p : other.pieces) { + this.pieces.add(new Piece(p.getX(), p.getY(), p.getType(), p.isWhite())); + } + this.selectedX = other.selectedX; + this.selectedY = other.selectedY; + this.turnNumber = other.turnNumber; + this.isTurnWhite = other.isTurnWhite; + this.highlightedPositions = new HashSet<>(other.highlightedPositions); + this.enPassantTargetX = other.enPassantTargetX; // Copier l'état en passant + this.enPassantTargetY = other.enPassantTargetY; // Copier l'état en passant } - public int getHeight() { - return this.height; + public Board(String[] boardData) { + this(8, 8); + System.err.println("Board(String[] boardData) constructor is not fully implemented. Creates an empty 8x8 board."); + // TODO: Ajouter le parsing de enPassantTargetX/Y si sauvé dans le fichier } - public int getTurnNumber() { - return turnNumber; + public int getWidth() { return this.width; } + public int getHeight() { return this.height; } + public int getTurnNumber() { return this.turnNumber; } + public boolean isTurnWhite() { return this.isTurnWhite; } + public int getSelectedX() { return this.selectedX; } + public int getSelectedY() { return this.selectedY; } + public int getEnPassantTargetX() { return this.enPassantTargetX; } // Getter + public int getEnPassantTargetY() { return this.enPassantTargetY; } // Getter + + + public List getPieces() { + return new ArrayList<>(this.pieces); } - public boolean isTurnWhite() { - return isTurnWhite; + Piece getPieceAt(int x, int y) { + if (x < 0 || x >= width || y < 0 || y >= height) { + return null; + } + for (Piece piece : this.pieces) { + if (piece.getX() == x && piece.getY() == y) { + return piece; + } + } + return null; } - public int getSelectedX() { - return selectedX; + public boolean isSelected(int x, int y) { + return this.selectedX == x && this.selectedY == y; } - public int getSelectedY() { - return selectedY; + public boolean isHighlighted(int x, int y) { + String position = x + "," + y; + return this.highlightedPositions.contains(position); + } + + private void setHighlightedPositions(Piece piece) { + this.highlightedPositions.clear(); + if (piece != null) { + Set validMoves = Move.calculateValidMoves(this, piece); + this.highlightedPositions.addAll(validMoves); + } + } + + private void clearHighlightedPositions() { + if (!this.highlightedPositions.isEmpty()) { + this.highlightedPositions.clear(); + } } public void setPiece(boolean isWhite, PieceType type, int x, int y) { if (x < 0 || x >= width || y < 0 || y >= height) { - System.out.println("setPiece: Out of bounds at (" + x + "," + y + ")"); + System.err.println("setPiece Error: Position (" + x + "," + y + ") is out of bounds."); return; } - pieces.removeIf(piece -> { - if (piece.getX() == x && piece.getY() == y) { - System.out.println("setPiece: Removing piece at (" + x + "," + y + "): " + piece.getType() + ", isWhite=" + piece.isWhite()); - return true; - } - return false; - }); - System.out.println("setPiece: Adding " + type + ", isWhite=" + isWhite + " at (" + x + "," + y + ")"); - pieces.add(new Piece(x, y, type, isWhite)); + Piece existingPiece = getPieceAt(x, y); + if (existingPiece != null) { + this.pieces.remove(existingPiece); + } + if (type != null) { + Piece newPiece = new Piece(x, y, type, isWhite); + this.pieces.add(newPiece); + } } public void populateBoard() { + if (width != 8 || height != 8) { + System.err.println("populateBoard Warning: Method designed for 8x8 board, current size is " + width + "x" + height + "."); + } cleanBoard(); - for (int x = 0; x < 8; x++) { + for (int x = 0; x < width; x++) { setPiece(true, PieceType.Pawn, x, 6); + setPiece(false, PieceType.Pawn, x, 1); } setPiece(true, PieceType.Rook, 0, 7); setPiece(true, PieceType.Knight, 1, 7); @@ -76,9 +139,7 @@ public class Board { setPiece(true, PieceType.Bishop, 5, 7); setPiece(true, PieceType.Knight, 6, 7); setPiece(true, PieceType.Rook, 7, 7); - for (int x = 0; x < 8; x++) { - setPiece(false, PieceType.Pawn, x, 1); - } + setPiece(false, PieceType.Rook, 0, 0); setPiece(false, PieceType.Knight, 1, 0); setPiece(false, PieceType.Bishop, 2, 0); @@ -90,15 +151,161 @@ public class Board { } public void cleanBoard() { - System.out.println("cleanBoard: Clearing all pieces"); - pieces.clear(); - selectedX = -1; - selectedY = -1; - turnNumber = 0; - isTurnWhite = true; - Move.clearHighlightedPositions(); + this.pieces.clear(); + this.selectedX = -1; + this.selectedY = -1; + this.turnNumber = 0; + this.isTurnWhite = true; + this.clearHighlightedPositions(); + this.enPassantTargetX = -1; // Réinitialiser aussi ici + this.enPassantTargetY = -1; // Réinitialiser aussi ici } + public void userTouch(int x, int y) { + if (x < 0 || x >= width || y < 0 || y >= height) { + this.selectedX = -1; + this.selectedY = -1; + this.clearHighlightedPositions(); + return; + } + + Piece clickedPiece = getPieceAt(x, y); + + if (selectedX == -1 && selectedY == -1) { + if (clickedPiece != null) { + if (clickedPiece.isWhite() == this.isTurnWhite) { + this.selectedX = x; + this.selectedY = y; + this.setHighlightedPositions(clickedPiece); + } else { + this.clearHighlightedPositions(); + } + } else { + this.clearHighlightedPositions(); + } + } + else { + Piece selectedPiece = getPieceAt(selectedX, selectedY); + + if (selectedPiece == null) { + System.err.println("Error: Selected piece at (" + selectedX + "," + selectedY + ") is missing!"); + this.selectedX = -1; + this.selectedY = -1; + this.clearHighlightedPositions(); + return; + } + + if (x == selectedX && y == selectedY) { + this.selectedX = -1; + this.selectedY = -1; + this.clearHighlightedPositions(); + } + else if (clickedPiece != null && clickedPiece.isWhite() == selectedPiece.isWhite()) { + this.selectedX = x; + this.selectedY = y; + this.setHighlightedPositions(clickedPiece); + } + else { + if (isHighlighted(x, y)) { + Piece capturedPiece = clickedPiece; // Peut être null pour en passant + Move move = new Move(selectedX, selectedY, x, y, selectedPiece, capturedPiece); + playMove(move); + + this.selectedX = -1; + this.selectedY = -1; + this.clearHighlightedPositions(); + + } else { + this.selectedX = -1; + this.selectedY = -1; + this.clearHighlightedPositions(); + } + } + } + } + + public void playMove(Move move) { + if (move == null || move.getPieceMoved() == null) { + System.err.println("playMove Error: Invalid move object provided."); + return; + } + + // *** Début Modification En Passant *** + // Réinitialiser la cible en passant AVANT de traiter le coup + int previousEnPassantX = this.enPassantTargetX; + int previousEnPassantY = this.enPassantTargetY; + this.enPassantTargetX = -1; + this.enPassantTargetY = -1; + // *** Fin Modification En Passant *** + + Piece pieceToMove = getPieceAt(move.getFromX(), move.getFromY()); + Piece capturedPiece = move.getPieceCaptured(); // La pièce sur la case d'arrivée + + if (pieceToMove == null || pieceToMove.getType() != move.getPieceMoved().getType() || pieceToMove.isWhite() != move.getPieceMoved().isWhite()) { + System.err.println("playMove Error: Mismatch between move data and piece found at source square (" + move.getFromX() + "," + move.getFromY() + "). Aborting move."); + return; + } + + // *** Début Modification En Passant : Détection de la capture *** + boolean isEnPassantCapture = false; + if (pieceToMove.getType() == PieceType.Pawn && + move.getToX() == previousEnPassantX && + move.getToY() == previousEnPassantY && + capturedPiece == null) { // La case cible EP est vide + isEnPassantCapture = true; + // Trouver et retirer le pion capturé qui est DERRIERE la case cible + int capturedPawnY = move.getFromY(); // Il est sur la même ligne que le pion qui capture + Piece pawnToRemove = getPieceAt(move.getToX(), capturedPawnY); + if (pawnToRemove != null && pawnToRemove.getType() == PieceType.Pawn && pawnToRemove.isWhite() != pieceToMove.isWhite()) { + this.pieces.remove(pawnToRemove); + System.out.println("En Passant capture: removed pawn at (" + move.getToX() + "," + capturedPawnY + ")"); + } else { + System.err.println("playMove Error: En passant capture failed, couldn't find pawn to remove at ("+ move.getToX() + "," + capturedPawnY + ")"); + // Que faire? Annuler le coup? Pour l'instant on continue... + } + } + // *** Fin Modification En Passant : Détection de la capture *** + + + // Retirer la pièce capturée "normalement" (si pas en passant capture et pièce sur case cible) + if (capturedPiece != null && !isEnPassantCapture) { + boolean removed = this.pieces.remove(capturedPiece); + if (!removed) { + Piece actualPieceAtTarget = getPieceAt(move.getToX(), move.getToY()); + if (actualPieceAtTarget != null && actualPieceAtTarget.getType() == capturedPiece.getType() && actualPieceAtTarget.isWhite() == capturedPiece.isWhite()) { + removed = this.pieces.remove(actualPieceAtTarget); + } + } + if (!removed) { + System.err.println("playMove Warning: Could not remove captured piece " + capturedPiece.getType() + " at (" + move.getToX() + "," + move.getToY() + ")"); + } + } + + // Mettre à jour la position de la pièce déplacée + pieceToMove.setPosition(move.getToX(), move.getToY()); + + + // *** Début Modification En Passant : Détection du double pas *** + // Si le coup était un double pas de pion, définir la nouvelle cible en passant + if (pieceToMove.getType() == PieceType.Pawn && Math.abs(move.getToY() - move.getFromY()) == 2) { + this.enPassantTargetX = move.getToX(); + // La case cible est celle que le pion a sautée + this.enPassantTargetY = (move.getFromY() + move.getToY()) / 2; + System.out.println("En Passant target set at (" + this.enPassantTargetX + "," + this.enPassantTargetY + ")"); + } + // *** Fin Modification En Passant : Détection du double pas *** + + + // Mettre à jour le tour et le joueur actif + this.turnNumber++; + this.isTurnWhite = !this.isTurnWhite; + + System.out.println(this.toString()); + System.out.println("Turn " + this.turnNumber + ". " + (this.isTurnWhite ? "White" : "Black") + " to move."); + } + + + @Override public String toString() { String[][] boardRep = new String[height][width]; for (int y = 0; y < height; y++) { @@ -106,141 +313,41 @@ public class Board { boardRep[y][x] = "."; } } - for (Piece piece : pieces) { - String symbol = piece.getType().getSummary(); - symbol = piece.isWhite() ? symbol.toUpperCase() : symbol.toLowerCase(); - boardRep[piece.getY()][piece.getX()] = symbol; + for (Piece piece : this.pieces) { + if (piece.getY() >= 0 && piece.getY() < height && piece.getX() >= 0 && piece.getX() < width) { + String symbol = piece.getType().getSummary(); + symbol = piece.isWhite() ? symbol.toUpperCase() : symbol.toLowerCase(); + boardRep[piece.getY()][piece.getX()] = symbol; + } } StringBuilder sb = new StringBuilder(); + sb.append(" "); + for(int x=0; x getPieces() { - return new ArrayList<>(pieces); - } - - public void userTouch(int x, int y) { - System.out.println("userTouch called with x=" + x + ", y=" + y); - if (x < 0 || x >= width || y < 0 || y >= height) { - System.out.println("Click out of bounds"); - return; - } - - Piece clickedPiece = getPieceAt(x, y); - if (selectedX == -1 && selectedY == -1) { - if (clickedPiece != null) { - System.out.println("Selected piece at (" + x + "," + y + "): " + clickedPiece.getType() + ", isWhite=" + clickedPiece.isWhite()); - selectedX = x; - selectedY = y; - Move.setHighlightedPositions(this, clickedPiece); - } else { - System.out.println("No piece at (" + x + "," + y + ")"); - } - } else { - System.out.println("Selected position: (" + selectedX + "," + selectedY + ")"); - Piece selectedPiece = getPieceAt(selectedX, selectedY); - if (selectedPiece == null) { - System.out.println("No piece at (" + selectedX + "," + selectedY + ")"); - selectedX = -1; - selectedY = -1; - Move.clearHighlightedPositions(); - return; - } - // If clicking the same position, unselect - if (selectedX == x && selectedY == y) { - System.out.println("Unselecting piece"); - selectedX = -1; - selectedY = -1; - Move.clearHighlightedPositions(); - } - // If clicking another piece of the same color, select that piece instead - else if (clickedPiece != null && clickedPiece.isWhite() == selectedPiece.isWhite()) { - System.out.println("Switching selection to piece at (" + x + "," + y + "): " + clickedPiece.getType() + ", isWhite=" + clickedPiece.isWhite()); - selectedX = x; - selectedY = y; - Move.setHighlightedPositions(this, clickedPiece); - } - // Otherwise, attempt to move - else { - if (!selectedPiece.canMoveTo(x, y, this)) { - System.out.println("Invalid move for " + selectedPiece.getType() + " from (" + - selectedX + "," + selectedY + ") to (" + x + "," + y + ")"); - selectedX = -1; - selectedY = -1; - Move.clearHighlightedPositions(); - return; - } - System.out.println("Moving piece from (" + selectedX + "," + selectedY + ") to (" + x + "," + y + ")"); - Piece pieceAtDestination = getPieceAt(x, y); - if (pieceAtDestination != null) { - System.out.println("Capturing piece at destination: " + pieceAtDestination.getType() + ", isWhite=" + pieceAtDestination.isWhite()); - } else { - System.out.println("No piece at destination"); - } - Move move = new Move(selectedX, selectedY, x, y, selectedPiece, pieceAtDestination); - playMove(move); - selectedX = -1; - selectedY = -1; - Move.clearHighlightedPositions(); - } - } - } - - public boolean isSelected(int x, int y) { - return selectedX == x && selectedY == y; - } - - public boolean isHighlighted(int x, int y) { - return Move.isHighlighted(x, y); - } - - public void playMove(Move move) { - System.out.println("playMove: Moving from (" + move.getFromX() + "," + move.getFromY() + ") to (" + - move.getToX() + "," + move.getToY() + ")"); - System.out.println("Before move - Pieces: " + pieces.size()); - for (Piece piece : pieces) { - System.out.println("Piece at (" + piece.getX() + "," + piece.getY() + "): " + piece.getType() + ", isWhite=" + piece.isWhite()); - } - pieces.removeIf(piece -> piece.getX() == move.getToX() && piece.getY() == move.getToY()); - pieces.removeIf(piece -> piece.getX() == move.getFromX() && piece.getY() == move.getFromY()); - Piece pieceMoved = move.getPieceMoved(); - pieces.add(new Piece(move.getToX(), move.getToY(), pieceMoved.getType(), pieceMoved.isWhite())); - turnNumber++; - isTurnWhite = !isTurnWhite; - System.out.println("After move - Pieces: " + pieces.size()); - for (Piece piece : pieces) { - System.out.println("Piece at (" + piece.getX() + "," + piece.getY() + "): " + piece.getType() + ", isWhite=" + piece.isWhite()); - } - } - - private Piece getPieceAt(int x, int y) { - for (Piece piece : pieces) { - if (piece.getX() == x && piece.getY() == y) { - return piece; - } - } - return null; - } - public String[] toFileRep() { + System.err.println("Board.toFileRep() is not implemented."); + // TODO: Ajouter la sauvegarde de enPassantTargetX/Y return null; } - public Board(String[] array) { - // TODO + public boolean undoLastMove() { + System.err.println("Board.undoLastMove() is not implemented."); + // TODO: Ajouter la restauration de enPassantTargetX/Y et du pion capturé en passant + return false; } - public void undoLastMove() { - // TODO - } - - public Board(Board board) { - // TODO - } -} +} \ No newline at end of file diff --git a/src/backend/Game.java b/src/backend/Game.java index e9736ec..e85382e 100644 --- a/src/backend/Game.java +++ b/src/backend/Game.java @@ -1,128 +1,353 @@ package backend; -import windowInterface.MyInterface; +import windowInterface.MyInterface; // Assurez-vous que ce chemin d'importation est correct +/** + * Représente la logique principale du jeu d'échecs. + * Gère l'état du plateau (Board), les joueurs (humain/IA), le déroulement des tours, + * et interagit avec l'interface utilisateur (MyInterface). + * Fonctionne dans un thread séparé pour permettre le jeu de l'IA sans bloquer l'UI. + */ public class Game extends Thread { - private AutoPlayer aiPlayer; - private Board board; + // Dépendances + private final Board board; // L'état actuel de l'échiquier (final car l'instance Board ne change pas, seul son état interne) + private final AutoPlayer aiPlayer; // Le moteur d'IA + private final MyInterface mjf; // Référence à l'interface graphique pour les mises à jour - private MyInterface mjf; - private int COL_NUM = 8; - private int LINE_NUM = 8; - private int loopDelay = 250; - boolean[] activationAIFlags; + // Constantes de configuration + private static final int DEFAULT_WIDTH = 8; + private static final int DEFAULT_HEIGHT = 8; + private static final int DEFAULT_LOOP_DELAY = 250; // ms entre les vérifications de l'IA + // État de contrôle du jeu + private int loopDelay; // Délai actuel de la boucle (peut être modifié) + private final boolean[] activationAIFlags; // Index 0 pour Noir (isWhite=false), Index 1 pour Blanc (isWhite=true) + + /** + * Constructeur de la partie. + * Initialise le plateau, l'IA, les flags de contrôle et la configuration initiale. + * + * @param mjfParam Référence à l'interface graphique pour pouvoir la mettre à jour. Doit être non-null. + */ public Game(MyInterface mjfParam) { - mjf = mjfParam; - board = new Board(COL_NUM, LINE_NUM); - loopDelay = 250; - LINE_NUM = 8; - COL_NUM = 8; - activationAIFlags = new boolean[2]; - aiPlayer = new AutoPlayer(); + if (mjfParam == null) { + throw new IllegalArgumentException("MyInterface parameter cannot be null."); + } + this.mjf = mjfParam; + this.board = new Board(DEFAULT_WIDTH, DEFAULT_HEIGHT); // Crée un nouveau plateau 8x8 + this.board.populateBoard(); // Remplit le plateau avec la configuration initiale + + this.aiPlayer = new AutoPlayer(); // Initialise le joueur IA + this.activationAIFlags = new boolean[2]; // Tableau pour savoir si l'IA est active pour chaque couleur + this.activationAIFlags[0] = false; // IA désactivée pour Noir par défaut + this.activationAIFlags[1] = false; // IA désactivée pour Blanc par défaut + + this.loopDelay = DEFAULT_LOOP_DELAY; // Définit le délai initial de la boucle du thread + System.out.println("Game initialized. Board populated. White's turn."); + // Afficher l'état initial + System.out.println(this.board.toString()); } + // --- Getters délégués au Board --- + + /** @return La largeur du plateau. */ public int getWidth() { return board.getWidth(); } + /** @return La hauteur du plateau. */ public int getHeight() { return board.getHeight(); } - public void run() { - while(true) { - aiPlayerTurn(); - mjf.update(board.getTurnNumber(), board.isTurnWhite()); - try { - Thread.sleep(loopDelay); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - } - - private boolean isAITurn() { - return activationAIFlags[board.isTurnWhite()?1:0]; - } - - private void aiPlayerTurn() { - if(isAITurn()) { - board.playMove(aiPlayer.computeBestMove(new Board(board))); - } - } - - public void clickCoords(int x, int y) { - int width = this.getWidth(); - int height = this.getHeight(); - if(0>x || 0>y || x>width || y>height) { - System.out.println("Click out of bounds"); - return; - } - if(!isAITurn()) { - - if (board.getSelectedX() == -1 && board.getSelectedY() == -1) { - Piece piece = null; - for (Piece p : board.getPieces()) { - if (p.getX() == x && p.getY() == y) { - piece = p; - break; - } - } - if (piece != null) { - - boolean isWhiteTurn = board.isTurnWhite(); - if (piece.isWhite() != isWhiteTurn) { - System.out.println("Cannot select a" + (piece.isWhite() ? "white" : "black") + "piece on" + (isWhiteTurn ? "White" : "Black") + "'s turn"); - return; - } - } - - } - board.userTouch(x, y); - } - } - - public void setPiece(boolean isWhite, PieceType type, int x, int y) { - board.setPiece(isWhite, type, x, y); - } - - public String[] getFileRepresentation() { - return board.toFileRep(); - } - - public void setLoopDelay(int delay) { - this.loopDelay = delay; - } - - public void setDefaultSetup() { - board.cleanBoard(); - board.populateBoard(); - } - - public void setBoard(String[] array) { - board = new Board(array); - } - + /** + * @return Une collection itérable des pièces actuellement sur le plateau. + * Renvoie une copie pour éviter les modifications externes non contrôlées. + */ public Iterable getPieces() { return board.getPieces(); } + /** + * Vérifie si la case (x, y) correspond à la pièce actuellement sélectionnée. + * @param x Coordonnée x. + * @param y Coordonnée y. + * @return true si la pièce à (x,y) est sélectionnée, false sinon. + */ public boolean isSelected(int x, int y) { return board.isSelected(x, y); } + /** + * Vérifie si la case (x, y) est mise en surbrillance comme destination possible. + * @param x Coordonnée x. + * @param y Coordonnée y. + * @return true si la case (x,y) est en surbrillance, false sinon. + */ public boolean isHighlighted(int x, int y) { return board.isHighlighted(x, y); } - public void undoLastMove() { - board.undoLastMove(); + /** @return true si c'est le tour des blancs, false si c'est le tour des noirs. */ + public boolean isWhiteTurn() { + return board.isTurnWhite(); + } + + /** @return Le numéro du tour actuel (commence à 0 ou 1 selon l'implémentation de Board). */ + public int getTurnNumber() { + return board.getTurnNumber(); + } + + // --- Logique principale du Thread --- + + /** + * Boucle principale du jeu exécutée dans le thread. + * Vérifie périodiquement si c'est au tour de l'IA de jouer, + * met à jour l'interface graphique et attend. + */ + @Override + public void run() { + System.out.println("Game thread started."); + while (!Thread.currentThread().isInterrupted()) { // Continue tant que le thread n'est pas interrompu + try { + // 1. Gérer le tour de l'IA si applicable + aiPlayerTurn(); + + // 2. Mettre à jour l'interface graphique + // (Numéro de tour, à qui le tour, et potentiellement repaint) + // On le fait à chaque itération pour refléter l'état même si aucun coup n'est joué. + mjf.update(board.getTurnNumber(), board.isTurnWhite()); + // Note: mjf.update() devrait idéalement déclencher un repaint dans l'UI. + + // 3. Pause avant la prochaine vérification + Thread.sleep(loopDelay); + + } catch (InterruptedException e) { + System.err.println("Game thread interrupted during sleep or operation."); + Thread.currentThread().interrupt(); // Important: Rétablir le statut d'interruption + // Sortir de la boucle car le thread doit s'arrêter + } catch (Exception e) { + // Capturer d'autres exceptions pour la robustesse + System.err.println("Unexpected error in game loop: " + e.getMessage()); + e.printStackTrace(); + // Optionnel : Mettre une pause plus longue pour éviter de spammer les logs en cas d'erreur répétée + try { Thread.sleep(5000); } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } + } + } + System.out.println("Game thread finished."); } + /** + * Vérifie si le joueur dont c'est le tour est actuellement contrôlé par l'IA. + * + * @return true si c'est le tour de l'IA, false si c'est un tour humain. + */ + private boolean isAITurn() { + int playerIndex = board.isTurnWhite() ? 1 : 0; // 1 pour Blanc, 0 pour Noir + return activationAIFlags[playerIndex]; + } + + /** + * Si c'est le tour de l'IA, lui demande de calculer un coup et le joue sur le plateau. + */ + private void aiPlayerTurn() { + if (isAITurn()) { + String colorTurn = board.isTurnWhite() ? "White" : "Black"; + System.out.println("AI's turn (" + colorTurn + "). Computing move..."); + + // Créer une copie *indépendante* du plateau pour l'analyse par l'IA. + // Ceci est crucial pour que l'IA puisse explorer des coups sans modifier l'état réel du jeu. + Board boardCopy = new Board(board); // Utilise le constructeur de copie de Board + + // Demander à l'IA de calculer le meilleur coup sur la copie + Move bestMove = aiPlayer.computeBestMove(boardCopy); + + // Si l'IA retourne un coup (elle n'est pas échec et mat ou pat) + if (bestMove != null) { + System.out.println("AI computed move: " + bestMove.getPieceMoved().getType() + + " from (" + bestMove.getFromX() + "," + bestMove.getFromY() + + ") to (" + bestMove.getToX() + "," + bestMove.getToY() + ")"); + // Jouer le coup sur le plateau *principal* + board.playMove(bestMove); + // playMove met à jour l'état interne du board (pièces, tour, etc.) + // L'UI sera mise à jour à la prochaine itération de run() + } else { + // L'IA n'a trouvé aucun coup (peut indiquer échec et mat, pat, ou une erreur) + System.err.println("AI returned null move! Possible stalemate, checkmate, or error in AI for " + colorTurn + "."); + // Optionnel: Désactiver l'IA ou afficher un message de fin de partie. + // Pour l'instant, le jeu continue (ou se bloque si c'est vraiment mat/pat). + } + } + } + + // --- Gestion des interactions utilisateur --- + + /** + * Gère un clic de l'utilisateur sur la case (x, y) de l'échiquier. + * Vérifie si le clic est dans les limites et si ce n'est pas au tour de l'IA. + * Si les conditions sont remplies, délègue toute la logique d'interaction + * (sélection, désélection, déplacement) à la méthode `userTouch` du `Board`. + * + * @param x Coordonnée x (colonne) de la case cliquée. + * @param y Coordonnée y (ligne) de la case cliquée. + */ + public void clickCoords(int x, int y) { + // System.out.println("Game received click at (" + x + "," + y + ")"); // Debug + + // 1. Ignorer si clic hors limites + if (x < 0 || x >= getWidth() || y < 0 || y >= getHeight()) { + System.out.println("Click ignored: out of bounds."); + return; + } + + // 2. Ignorer si c'est le tour de l'IA (le joueur humain ne peut pas interagir) + if (isAITurn()) { + System.out.println("Click ignored: AI's turn."); + return; + } + + // 3. Si c'est un tour humain et clic dans les limites, déléguer au Board + // Board.userTouch gère la sélection, la mise en surbrillance, la désélection et le jeu du coup. + board.userTouch(x, y); + + // L'état du jeu (sélection, highlights, pièces) peut avoir changé. + // La boucle `run` mettra à jour l'interface graphique. + // Si une mise à jour VISUELLE immédiate est cruciale après chaque clic humain : + // mjf.update(board.getTurnNumber(), board.isTurnWhite()); // Forcer update + repaint + } + + // --- Méthodes de Contrôle et Configuration --- + + /** + * Active ou désactive l'IA pour la couleur spécifiée. + * + * @param isWhite true pour contrôler l'IA blanche, false pour l'IA noire. + */ public void toggleAI(boolean isWhite) { - this.activationAIFlags[isWhite?1:0] = !this.activationAIFlags[isWhite?1:0]; + int playerIndex = isWhite ? 1 : 0; + activationAIFlags[playerIndex] = !activationAIFlags[playerIndex]; // Bascule l'état + String color = isWhite ? "White" : "Black"; + String status = activationAIFlags[playerIndex] ? "enabled" : "disabled"; + System.out.println("AI for " + color + " player is now " + status + "."); + // Si on active l'IA et que c'est son tour, elle jouera à la prochaine itération de run() } -} + /** + * Modifie le délai (en millisecondes) entre les itérations de la boucle du thread. + * Un délai plus court rend l'IA plus réactive (si elle calcule vite), + * un délai plus long économise des ressources CPU. + * + * @param delay Le nouveau délai en millisecondes (doit être positif). + */ + public void setLoopDelay(int delay) { + if (delay > 0) { + this.loopDelay = delay; + System.out.println("Game loop delay set to " + delay + " ms."); + } else { + System.err.println("Invalid loop delay requested: " + delay + ". Must be positive."); + } + } + + /** + * Réinitialise le plateau à la configuration de départ standard des échecs. + * Efface l'état actuel et remet les pièces à leur place initiale. + */ + public void setDefaultSetup() { + System.out.println("Setting default board setup..."); + board.cleanBoard(); // Nettoie le plateau (pièces, sélection, highlights, tour) + board.populateBoard(); // Met en place les pièces + System.out.println("Board reset to initial state."); + System.out.println(this.board.toString()); + // Mettre à jour l'interface immédiatement pour refléter le changement + mjf.update(board.getTurnNumber(), board.isTurnWhite()); + } + + // --- Méthodes déléguées au Board (pour manipulation externe, sauvegarde/chargement, etc.) --- + + /** + * Place directement une pièce sur le plateau. + * Attention : Utiliser principalement pour le débogage ou la mise en place de positions spécifiques. + * Écrase toute pièce existante à cet endroit. + * + * @param isWhite Couleur de la pièce (true=blanc). + * @param type Type de la pièce (Pawn, Rook...). + * @param x Coordonnée x. + * @param y Coordonnée y. + */ + public void setPiece(boolean isWhite, PieceType type, int x, int y) { + board.setPiece(isWhite, type, x, y); + // Mettre à jour l'interface pour voir le changement + mjf.update(board.getTurnNumber(), board.isTurnWhite()); + } + + /** + * Récupère une représentation du plateau sous forme de tableau de chaînes de caractères. + * Utile pour la sauvegarde ou le débogage. + * (Nécessite que la méthode `toFileRep` soit implémentée dans la classe Board). + * + * @return Un tableau de String représentant l'état du plateau, ou null si non supporté. + */ + public String[] getFileRepresentation() { + // TODO: Implémenter Board.toFileRep() pour que cela fonctionne. + System.out.println("Getting file representation from board..."); + return board.toFileRep(); + } + + /** + * Configure l'état du plateau à partir d'une représentation sous forme de tableau de chaînes. + * Remplace complètement l'état actuel du plateau. + * (Nécessite que le constructeur `Board(String[] array)` soit implémenté). + * + * @param boardData Le tableau de String décrivant le nouvel état du plateau. + */ + public void setBoard(String[] boardData) { + System.out.println("Setting board state from string array..."); + // TODO: Implémenter le constructeur Board(String[] array) pour que cela fonctionne. + // Ceci remplacera l'instance actuelle du board ou modifiera son état interne. + // Si on remplace l'instance, il faut s'assurer que la référence 'board' est mise à jour. + // Exemple (si le constructeur existe) : + // this.board = new Board(boardData); + try { + // Tentative de créer un nouveau board (si le constructeur existe) + // Note: Ceci ne fonctionnera que si 'board' n'est PAS final. + // Si 'board' est final, il faudrait une méthode dans Board pour charger l'état. + // Exemple: this.board.loadFromArray(boardData); + System.err.println("setBoard(String[]) is not fully implemented yet (requires Board constructor or load method)."); + // Pour l'instant, on ne fait rien pour éviter une erreur si Board(String[]) n'existe pas + // this.board = new Board(boardData); + + } catch (Exception e) { + System.err.println("Failed to set board from string array: " + e.getMessage()); + e.printStackTrace(); + } + // Dans tous les cas, mettre à jour l'interface + mjf.update(this.board.getTurnNumber(), this.board.isTurnWhite()); + } + + /** + * Tente d'annuler le dernier coup joué. + * La possibilité d'annuler dépend de si le `Board` gère un historique des coups. + * (Nécessite que la méthode `undoLastMove` soit implémentée dans la classe Board). + */ + public void undoLastMove() { + // Optionnel: Ajouter une logique pour empêcher l'annulation pendant le tour de l'IA + // if (isAITurn()) { + // System.out.println("Undo ignored: AI is thinking or has just played."); + // return; + // } + + System.out.println("Requesting undo last move..."); + // TODO: Implémenter Board.undoLastMove() pour que cela fonctionne. + boolean success = board.undoLastMove(); // Supposons que undoLastMove renvoie un booléen + if (success) { + System.out.println("Last move undone successfully."); + System.out.println(this.board.toString()); // Afficher le nouvel état + } else { + System.out.println("Undo failed (no history or cannot undo)."); + } + // Mettre à jour l'interface quelle que soit l'issue + mjf.update(board.getTurnNumber(), board.isTurnWhite()); + } + +} \ No newline at end of file diff --git a/src/backend/Move.java b/src/backend/Move.java index 4578bbe..8b1e9c3 100644 --- a/src/backend/Move.java +++ b/src/backend/Move.java @@ -1,225 +1,71 @@ package backend; +import java.util.HashSet; +import java.util.Set; + public class Move { - private int fromX; - private int fromY; - private int toX; - private int toY; - private Piece pieceMoved; - private Piece pieceCaptured; - private static String[] highlightedPositions = new String[0]; // Store highlighted positions + private final int fromX; + private final int fromY; + private final int toX; + private final int toY; + private final Piece pieceMoved; + private final Piece pieceCaptured; public Move(int fromX, int fromY, int toX, int toY, Piece pieceMoved, Piece pieceCaptured) { this.fromX = fromX; this.fromY = fromY; this.toX = toX; this.toY = toY; + if (pieceMoved == null) { + throw new IllegalArgumentException("pieceMoved cannot be null in a Move."); + } this.pieceMoved = pieceMoved; this.pieceCaptured = pieceCaptured; } - public int getFromX() { - return fromX; - } + public int getFromX() { return fromX; } + public int getFromY() { return fromY; } + public int getToX() { return toX; } + public int getToY() { return toY; } + public Piece getPieceMoved() { return pieceMoved; } + public Piece getPieceCaptured() { return pieceCaptured; } - public int getFromY() { - return fromY; - } + public static Set calculateValidMoves(Board board, Piece piece) { + Set validMoves = new HashSet<>(); - public int getToX() { - return toX; - } - - public int getToY() { - return toY; - } - - public Piece getPieceMoved() { - return pieceMoved; - } - - public Piece getPieceCaptured() { - return pieceCaptured; - } - - // Static method to calculate and set highlighted positions for a piece - public static void setHighlightedPositions(Board board, Piece piece) { - if (piece == null) { - highlightedPositions = new String[0]; - } else { - highlightedPositions = calculateValidMoves(board, piece); + if (piece == null || board == null) { + return validMoves; } - } - - // Static method to clear highlighted positions - public static void clearHighlightedPositions() { - highlightedPositions = new String[0]; - } - - // Static method to check if a position is highlighted - public static boolean isHighlighted(int x, int y) { - String position = x + "," + y; - for (int i = 0; i < highlightedPositions.length; i++) { - if (highlightedPositions[i] != null && highlightedPositions[i].equals(position)) { - return true; - } - } - return false; - } - - // Static method to calculate all valid moves for a piece - public static String[] calculateValidMoves(Board board, Piece piece) { - String[] validMoves = new String[32]; // Allocate space for up to 32 moves - int[] moveCount = new int[1]; // Use an array to simulate a mutable counter - moveCount[0] = 0; int x = piece.getX(); int y = piece.getY(); + switch (piece.getType()) { case Pawn: - calculatePawnMoves(board, piece, x, y, validMoves, moveCount); + calculatePawnMoves(board, piece, x, y, validMoves); break; case Rook: - calculateRookMoves(board, piece, x, y, validMoves, moveCount); + calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}); break; case Knight: - calculateKnightMoves(board, piece, x, y, validMoves, moveCount); + calculateKnightMoves(board, piece, x, y, validMoves); break; case Bishop: - calculateBishopMoves(board, piece, x, y, validMoves, moveCount); + calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{1, 1}, {1, -1}, {-1, 1}, {-1, -1}}); break; case Queen: - calculateQueenMoves(board, piece, x, y, validMoves, moveCount); + calculateSlidingMoves(board, piece, x, y, validMoves, new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}}); break; case King: - calculateKingMoves(board, piece, x, y, validMoves, moveCount); + calculateKingMoves(board, piece, x, y, validMoves); break; } - // Trim the array to the actual number of moves - String[] result = new String[moveCount[0]]; - for (int i = 0; i < moveCount[0]; i++) { - result[i] = validMoves[i]; - } - return result; + return validMoves; } - private static void calculatePawnMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - int direction = piece.isWhite() ? -1 : 1; // White pawns move up, black pawns move down - int startRow = piece.isWhite() ? 6 : 1; // Starting row for double move - - // Move forward one square - if (isValidPosition(board, x, y + direction) && getPieceAt(board, x, y + direction) == null) { - addMoveIfNotDuplicate(x, y + direction, validMoves, moveCount); - // Move forward two squares from starting position - if (y == startRow && getPieceAt(board, x, y + 2 * direction) == null) { - addMoveIfNotDuplicate(x, y + 2 * direction, validMoves, moveCount); - } - } - - // Capture diagonally - if (isValidPosition(board, x - 1, y + direction)) { - Piece target = getPieceAt(board, x - 1, y + direction); - if (target != null && target.isWhite() != piece.isWhite()) { - addMoveIfNotDuplicate(x - 1, y + direction, validMoves, moveCount); - } - } - if (isValidPosition(board, x + 1, y + direction)) { - Piece target = getPieceAt(board, x + 1, y + direction); - if (target != null && target.isWhite() != piece.isWhite()) { - addMoveIfNotDuplicate(x + 1, y + direction, validMoves, moveCount); - } - } - } - - private static void calculateRookMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - // Horizontal: left - for (int i = x - 1; i >= 0; i--) { - if (!addMoveIfValid(board, piece, i, y, validMoves, moveCount)) break; - } - // Horizontal: right - for (int i = x + 1; i < board.getWidth(); i++) { - if (!addMoveIfValid(board, piece, i, y, validMoves, moveCount)) break; - } - // Vertical: up - for (int j = y - 1; j >= 0; j--) { - if (!addMoveIfValid(board, piece, x, j, validMoves, moveCount)) break; - } - // Vertical: down - for (int j = y + 1; j < board.getHeight(); j++) { - if (!addMoveIfValid(board, piece, x, j, validMoves, moveCount)) break; - } - } - - private static void calculateKnightMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - int[][] knightMoves = { - {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, - {1, -2}, {1, 2}, {2, -1}, {2, 1} - }; - for (int i = 0; i < knightMoves.length; i++) { - int newX = x + knightMoves[i][0]; - int newY = y + knightMoves[i][1]; - addMoveIfValid(board, piece, newX, newY, validMoves, moveCount); - } - } - - private static void calculateBishopMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - // Diagonal: up-left - for (int i = 1; x - i >= 0 && y - i >= 0; i++) { - if (!addMoveIfValid(board, piece, x - i, y - i, validMoves, moveCount)) break; - } - // Diagonal: up-right - for (int i = 1; x + i < board.getWidth() && y - i >= 0; i++) { - if (!addMoveIfValid(board, piece, x + i, y - i, validMoves, moveCount)) break; - } - // Diagonal: down-left - for (int i = 1; x - i >= 0 && y + i < board.getHeight(); i++) { - if (!addMoveIfValid(board, piece, x - i, y + i, validMoves, moveCount)) break; - } - // Diagonal: down-right - for (int i = 1; x + i < board.getWidth() && y + i < board.getHeight(); i++) { - if (!addMoveIfValid(board, piece, x + i, y + i, validMoves, moveCount)) break; - } - } - - private static void calculateQueenMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - // Queen moves like a rook + bishop - calculateRookMoves(board, piece, x, y, validMoves, moveCount); - calculateBishopMoves(board, piece, x, y, validMoves, moveCount); - } - - private static void calculateKingMoves(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - for (int dx = -1; dx <= 1; dx++) { - for (int dy = -1; dy <= 1; dy++) { - if (dx == 0 && dy == 0) continue; - int newX = x + dx; - int newY = y + dy; - addMoveIfValid(board, piece, newX, newY, validMoves, moveCount); - } - } - } - - private static boolean addMoveIfValid(Board board, Piece piece, int x, int y, String[] validMoves, int[] moveCount) { - if (!isValidPosition(board, x, y)) return false; - Piece target = getPieceAt(board, x, y); - if (target == null || target.isWhite() != piece.isWhite()) { - addMoveIfNotDuplicate(x, y, validMoves, moveCount); - return target == null; // Continue if the square is empty, stop if we hit a piece - } - return false; // Stop if we hit a piece of the same color - } - - private static void addMoveIfNotDuplicate(int x, int y, String[] validMoves, int[] moveCount) { - if (moveCount[0] >= validMoves.length) return; // Prevent array overflow - String position = x + "," + y; - // Check for duplicates - for (int i = 0; i < moveCount[0]; i++) { - if (validMoves[i] != null && validMoves[i].equals(position)) { - return; // Position already exists - } - } - validMoves[moveCount[0]] = position; - moveCount[0]++; + private static void addMove(int x, int y, Set moves) { + moves.add(x + "," + y); } private static boolean isValidPosition(Board board, int x, int y) { @@ -227,12 +73,127 @@ public class Move { } private static Piece getPieceAt(Board board, int x, int y) { - Piece[] pieces = board.getPieces().toArray(new Piece[0]); - for (int i = 0; i < pieces.length; i++) { - if (pieces[i].getX() == x && pieces[i].getY() == y) { - return pieces[i]; + return board.getPieceAt(x, y); + } + + private static void calculateSlidingMoves(Board board, Piece piece, int x, int y, Set validMoves, int[][] directions) { + for (int[] dir : directions) { + int dx = dir[0]; + int dy = dir[1]; + for (int i = 1; ; i++) { + int nextX = x + i * dx; + int nextY = y + i * dy; + + if (!isValidPosition(board, nextX, nextY)) { + break; + } + + Piece targetPiece = getPieceAt(board, nextX, nextY); + + if (targetPiece == null) { + addMove(nextX, nextY, validMoves); + } else { + if (targetPiece.isWhite() != piece.isWhite()) { + addMove(nextX, nextY, validMoves); + } + break; + } } } - return null; } + + private static void calculatePawnMoves(Board board, Piece piece, int x, int y, Set validMoves) { + int forwardDirection = piece.isWhite() ? -1 : 1; + int startRow = piece.isWhite() ? 6 : 1; + + // Avance d'une case + int oneStepY = y + forwardDirection; + if (isValidPosition(board, x, oneStepY) && getPieceAt(board, x, oneStepY) == null) { + addMove(x, oneStepY, validMoves); + + // Avance de deux cases + int twoStepsY = y + 2 * forwardDirection; + if (y == startRow && isValidPosition(board, x, twoStepsY) && getPieceAt(board, x, twoStepsY) == null) { + addMove(x, twoStepsY, validMoves); + } + } + + // Captures diagonales normales + int[] captureXs = {x - 1, x + 1}; + for (int captureX : captureXs) { + if (isValidPosition(board, captureX, oneStepY)) { + Piece target = getPieceAt(board, captureX, oneStepY); + if (target != null && target.isWhite() != piece.isWhite()) { + addMove(captureX, oneStepY, validMoves); + } + } + } + + // *** Début Modification En Passant *** + // Vérifier la capture en passant + int enPassantTargetX = board.getEnPassantTargetX(); + int enPassantTargetY = board.getEnPassantTargetY(); + + if (enPassantTargetX != -1 && enPassantTargetY != -1) { // Si une cible EP existe + // Vérifier si la cible est sur la case diagonale avant + if (enPassantTargetY == oneStepY) { // La cible doit être sur la rangée devant le pion + if (Math.abs(x - enPassantTargetX) == 1) { // La cible doit être sur une colonne adjacente + // Vérifier qu'il y a bien un pion adverse à capturer à côté de nous (sur la case EP virtuelle) + // Le pion à capturer est sur (enPassantTargetX, y) + Piece adjacentPawn = getPieceAt(board, enPassantTargetX, y); + if (adjacentPawn != null && adjacentPawn.getType() == PieceType.Pawn && adjacentPawn.isWhite() != piece.isWhite()) { + addMove(enPassantTargetX, enPassantTargetY, validMoves); + System.out.println("En passant move calculated for pawn at ("+x+","+y+") to ("+enPassantTargetX+","+enPassantTargetY+")"); // Debug + } + } + } + } + // *** Fin Modification En Passant *** + } + + + private static void calculateKnightMoves(Board board, Piece piece, int x, int y, Set validMoves) { + int[][] knightMoves = { + {-2, -1}, {-2, 1}, {-1, -2}, {-1, 2}, + {1, -2}, {1, 2}, {2, -1}, {2, 1} + }; + for (int[] move : knightMoves) { + int nextX = x + move[0]; + int nextY = y + move[1]; + if (isValidPosition(board, nextX, nextY)) { + Piece target = getPieceAt(board, nextX, nextY); + if (target == null || target.isWhite() != piece.isWhite()) { + addMove(nextX, nextY, validMoves); + } + } + } + } + + private static void calculateKingMoves(Board board, Piece piece, int x, int y, Set validMoves) { + for (int dx = -1; dx <= 1; dx++) { + for (int dy = -1; dy <= 1; dy++) { + if (dx == 0 && dy == 0) continue; + + int nextX = x + dx; + int nextY = y + dy; + + if (isValidPosition(board, nextX, nextY)) { + Piece target = getPieceAt(board, nextX, nextY); + if (target == null || target.isWhite() != piece.isWhite()) { + addMove(nextX, nextY, validMoves); + } + } + } + } + // TODO: Ajouter calcul du roque ici + } + + @Override + public String toString() { + String captureStr = (pieceCaptured != null) ? "x" + pieceCaptured.getType().getSummary() : ""; + return pieceMoved.getType().getSummary() + + "(" + fromX + "," + fromY + ")" + + "-" + captureStr + + "(" + toX + "," + toY + ")"; + } } \ No newline at end of file diff --git a/src/backend/Piece.java b/src/backend/Piece.java index a8baae0..47c2ee8 100644 --- a/src/backend/Piece.java +++ b/src/backend/Piece.java @@ -1,163 +1,121 @@ package backend; -import java.util.ArrayList; +import java.util.Objects; public class Piece { private int x; private int y; - private PieceType type; - private boolean isWhite; - + private final PieceType type; + private final boolean isWhite; public Piece(int x, int y, PieceType type, boolean isWhite) { + if (type == null) { + throw new IllegalArgumentException("PieceType cannot be null."); + } this.x = x; this.y = y; this.type = type; this.isWhite = isWhite; - // this.hasMoved = false; // Initialiser si ajoute } - public int getX() { - return this.x; - } + public int getX() { return this.x; } + public int getY() { return this.y; } + public PieceType getType() { return this.type; } + public boolean isWhite() { return this.isWhite; } - public int getY() { - return this.y; - } - - public PieceType getType() { - return this.type; - } - - public boolean isWhite() { - return this.isWhite; - } - - // Methode pour mettre a jour la position (utilisee dans Board.playMove) - public void setPosition(int newX, int newY) { + void setPosition(int newX, int newY) { this.x = newX; this.y = newY; - // this.hasMoved = true; // Mettre à jour si la variable hasMoved existe } - - - public boolean canMoveTo(int toX, int toY, Board board) { - - if (x == toX && y == toY) { - return false; - } + if (board == null) return false; + if (this.x == toX && this.y == toY) return false; + if (!Move.isValidPosition(board, toX, toY)) return false; - - if (toX < 0 || toX >= board.getWidth() || toY < 0 || toY >= board.getHeight()) { + Piece pieceAtDestination = board.getPieceAt(toX, toY); + + if (pieceAtDestination != null && pieceAtDestination.isWhite() == this.isWhite) { return false; } - Piece pieceAtDestination = getPieceAt(board, toX, toY); + int dx = Math.abs(toX - this.x); + int dy = Math.abs(toY - this.y); - - if (pieceAtDestination != null && pieceAtDestination.isWhite() == this.isWhite) { - return false; - } - - // Calculs specifiques par type de piece - int dx = Math.abs(toX - x); - int dy = Math.abs(toY - y); // Note: dy calcule ici, mais canPawnMoveTo recalcule avec signe - - switch (type) { + switch (this.type) { case Pawn: - return canPawnMoveTo(toX, toY, board, pieceAtDestination); case Rook: - - return (dx == 0 || dy == 0) && isPathClear(x, y, toX, toY, board); + return (dx == 0 || dy == 0) && isPathClear(this.x, this.y, toX, toY, board); case Knight: - return (dx == 2 && dy == 1) || (dx == 1 && dy == 2); case Bishop: - - return (dx == dy) && isPathClear(x, y, toX, toY, board); + return (dx == dy) && isPathClear(this.x, this.y, toX, toY, board); case Queen: - - return (dx == 0 || dy == 0 || dx == dy) && isPathClear(x, y, toX, toY, board); + return (dx == 0 || dy == 0 || dx == dy) && isPathClear(this.x, this.y, toX, toY, board); case King: - return dx <= 1 && dy <= 1; default: - return false; + System.err.println("Erreur: Type de pièce inconnu dans canMoveTo: " + this.type); + return false; } } private boolean canPawnMoveTo(int toX, int toY, Board board, Piece pieceAtDestination) { - int deltaX = toX - x; - int deltaY = toY - y; - int forwardDirection = isWhite ? -1 : 1; - int startingRow = isWhite ? 6 : 1; + int deltaX = toX - this.x; + int deltaY = toY - this.y; + int forwardDirection = this.isWhite ? -1 : 1; + int startingRow = this.isWhite ? 6 : 1; - if (deltaX == 0 && deltaY == forwardDirection && pieceAtDestination == null) { - return true; } - - - if (deltaX == 0 && deltaY == 2 * forwardDirection && y == startingRow && pieceAtDestination == null) { - - int intermediateY = y + forwardDirection; - if (getPieceAt(board, x, intermediateY) == null) { + if (this.y == startingRow && deltaX == 0 && deltaY == 2 * forwardDirection && pieceAtDestination == null) { + if (board.getPieceAt(this.x, this.y + forwardDirection) == null) { return true; } } - - if (Math.abs(deltaX) == 1 && deltaY == forwardDirection && pieceAtDestination != null ) { - return true; } - - - return false; } - - private Piece getPieceAt(Board board, int targetX, int targetY) { - for (Piece p : board.getPieces()) { - if (p.getX() == targetX && p.getY() == targetY) { - return p; - } - } - return null; - } - - private boolean isPathClear(int fromX, int fromY, int toX, int toY, Board board) { int dx = toX - fromX; int dy = toY - fromY; int steps = Math.max(Math.abs(dx), Math.abs(dy)); + if (steps <= 1) return true; - - if (steps <= 1) { - return true; - } + int stepX = Integer.signum(dx); + int stepY = Integer.signum(dy); - - int stepX = Integer.signum(dx); - int stepY = Integer.signum(dy); + for (int i = 1; i < steps; i++) { + int checkX = fromX + i * stepX; + int checkY = fromY + i * stepY; + if (board.getPieceAt(checkX, checkY) != null) { + return false; + } + } + return true; + } - - for (int i = 1; i < steps; i++) { - int checkX = fromX + i * stepX; - int checkY = fromY + i * stepY; - if (getPieceAt(board, checkX, checkY) != null) { - - return false; - } - } + @Override + public String toString() { + return type.name() + "(" + (isWhite ? "W" : "B") + ")@[" + x + "," + y + "]"; + } - - return true; - } + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + Piece piece = (Piece) o; + return x == piece.x && y == piece.y && isWhite == piece.isWhite && type == piece.type; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, type, isWhite); + } } \ No newline at end of file