This commit is contained in:
cleme 2025-04-16 14:08:48 +02:00
parent cf06917fb5
commit b060000926
4 changed files with 271 additions and 166 deletions

View File

@ -4,21 +4,21 @@ 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
import java.util.Objects;
public class Board {
private final int width;
private final int height;
private final List<Piece> pieces;
private int selectedX;
private int selectedY;
private int turnNumber;
private boolean isTurnWhite;
private Set<String> highlightedPositions;
private int enPassantTargetX; // Nouvelle variable d'état
private int enPassantTargetY; // Nouvelle variable d'état
final int width;
final int height;
final List<Piece> pieces;
int selectedX;
int selectedY;
int turnNumber;
boolean isTurnWhite;
Set<String> highlightedPositions;
int enPassantTargetX;
int enPassantTargetY;
MoveHistory moveHistory;
public Board(int colNum, int lineNum) {
if (colNum <= 0 || lineNum <= 0) {
@ -32,8 +32,9 @@ public class Board {
this.turnNumber = 0;
this.isTurnWhite = true;
this.highlightedPositions = new HashSet<>();
this.enPassantTargetX = -1; // Initialisation
this.enPassantTargetY = -1; // Initialisation
this.enPassantTargetX = -1;
this.enPassantTargetY = -1;
this.moveHistory = new MoveHistory(this);
}
public Board(Board other) {
@ -48,14 +49,15 @@ public class Board {
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
this.enPassantTargetX = other.enPassantTargetX;
this.enPassantTargetY = other.enPassantTargetY;
this.moveHistory = new MoveHistory(this);
}
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
// TODO: Add parsing of enPassantTargetX/Y if saved in the file
}
public int getWidth() { return this.width; }
@ -64,9 +66,8 @@ public class Board {
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 int getEnPassantTargetX() { return this.enPassantTargetX; }
public int getEnPassantTargetY() { return this.enPassantTargetY; }
public List<Piece> getPieces() {
return new ArrayList<>(this.pieces);
@ -101,7 +102,7 @@ public class Board {
}
}
private void clearHighlightedPositions() {
void clearHighlightedPositions() {
if (!this.highlightedPositions.isEmpty()) {
this.highlightedPositions.clear();
}
@ -157,8 +158,9 @@ public class Board {
this.turnNumber = 0;
this.isTurnWhite = true;
this.clearHighlightedPositions();
this.enPassantTargetX = -1; // Réinitialiser aussi ici
this.enPassantTargetY = -1; // Réinitialiser aussi ici
this.enPassantTargetX = -1;
this.enPassantTargetY = -1;
this.moveHistory.clear();
}
public void userTouch(int x, int y) {
@ -207,7 +209,7 @@ public class Board {
}
else {
if (isHighlighted(x, y)) {
Piece capturedPiece = clickedPiece; // Peut être null pour en passant
Piece capturedPiece = clickedPiece; // Peut être null pour en passant
Move move = new Move(selectedX, selectedY, x, y, selectedPiece, capturedPiece);
playMove(move);
@ -230,44 +232,35 @@ public class Board {
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
Piece capturedPiece = move.getPieceCaptured();
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
capturedPiece == null) {
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
int capturedPawnY = move.getFromY();
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) {
@ -281,22 +274,16 @@ public class Board {
}
}
// 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 ***
moveHistory.addMove(move);
// Mettre à jour le tour et le joueur actif
this.turnNumber++;
this.isTurnWhite = !this.isTurnWhite;
@ -304,6 +291,9 @@ public class Board {
System.out.println("Turn " + this.turnNumber + ". " + (this.isTurnWhite ? "White" : "Black") + " to move.");
}
public boolean undoLastMove() {
return moveHistory.undoLastMove();
}
@Override
public String toString() {
@ -331,7 +321,6 @@ public class Board {
}
sb.append("\n");
}
// Afficher la cible en passant si elle existe (pour debug)
if (enPassantTargetX != -1) {
sb.append("En Passant Target: (").append(enPassantTargetX).append(",").append(enPassantTargetY).append(")\n");
}
@ -340,14 +329,7 @@ public class Board {
public String[] toFileRep() {
System.err.println("Board.toFileRep() is not implemented.");
// TODO: Ajouter la sauvegarde de enPassantTargetX/Y
// TODO: Add saving of enPassantTargetX/Y
return null;
}
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;
}
}

View File

@ -3,53 +3,53 @@ package backend;
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,
* 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.
* Fonctionne dans un thread séparé pour permettre le jeu de l'IA sans bloquer l'UI.
*/
public class Game extends Thread {
// Dépendances
private final Board board; // L'état actuel de l'échiquier (final car l'instance Board ne change pas, seul son état interne)
// 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 final MyInterface mjf; // Référence à l'interface graphique pour les mises à jour
// 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
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é)
// É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.
* 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.
* @param mjfParam Référence à l'interface graphique pour pouvoir la mettre à jour. Doit être non-null.
*/
public Game(MyInterface mjfParam) {
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 = 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.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
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
// Afficher l'état initial
System.out.println(this.board.toString());
}
// --- Getters délégués au Board ---
// --- Getters délégués au Board ---
/** @return La largeur du plateau. */
public int getWidth() {
@ -62,27 +62,27 @@ public class Game extends Thread {
}
/**
* @return Une collection itérable des pièces actuellement sur le plateau.
* Renvoie une copie pour éviter les modifications externes non contrôlées.
* @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<Piece> 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.
* 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.
* 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) {
@ -94,7 +94,7 @@ public class Game extends Thread {
return board.isTurnWhite();
}
/** @return Le numéro du tour actuel (commence à 0 ou 1 selon l'implémentation de Board). */
/** @return Le numéro du tour actuel (commence à 0 ou 1 selon l'implémentation de Board). */
public int getTurnNumber() {
return board.getTurnNumber();
}
@ -102,36 +102,36 @@ public class Game extends Thread {
// --- 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.
* 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
// 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é.
// 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.
// Note: mjf.update() devrait idéalement déclencher un repaint dans l'UI.
// 3. Pause avant la prochaine vérification
// 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
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
// 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(); }
}
}
@ -139,7 +139,7 @@ public class Game extends Thread {
}
/**
* Vérifie si le joueur dont c'est le tour est actuellement contrôlé par l'IA.
* 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.
*/
@ -156,26 +156,26 @@ public class Game extends Thread {
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.
// 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
// 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)
// 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()
// 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)
// 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.
// 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).
}
}
@ -184,13 +184,13 @@ public class Game extends Thread {
// --- 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`.
* 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.
* @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
@ -207,38 +207,38 @@ public class Game extends Thread {
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.
// 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 :
// 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 ---
// --- Méthodes de Contrôle et Configuration ---
/**
* Active ou désactive l'IA pour la couleur spécifiée.
* 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.
* @param isWhite true pour contrôler l'IA blanche, false pour l'IA noire.
*/
public void toggleAI(boolean isWhite) {
int playerIndex = isWhite ? 1 : 0;
activationAIFlags[playerIndex] = !activationAIFlags[playerIndex]; // Bascule l'état
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()
// 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.
* 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).
* @param delay Le nouveau délai en millisecondes (doit être positif).
*/
public void setLoopDelay(int delay) {
if (delay > 0) {
@ -250,103 +250,103 @@ public class Game extends Thread {
}
/**
* Réinitialise le plateau à la configuration de départ standard des échecs.
* Efface l'état actuel et remet les pièces à leur place initiale.
* 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
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
// 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.) ---
// --- 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.
* 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.
* @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
// 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).
* 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é.
* @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.
// 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é).
* 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.
* @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.
// 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)
// 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.
// 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
// 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
// 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).
* 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
// 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
// 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
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
// Mettre à jour l'interface quelle que soit l'issue
mjf.update(board.getTurnNumber(), board.isTurnWhite());
}

View File

@ -129,17 +129,17 @@ public class Move {
}
}
// *** Début Modification En Passant ***
// Vérifier la capture en passant
// *** 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)
// V<>rifier si la cible est sur la case diagonale avant
if (enPassantTargetY == oneStepY) { // La cible doit <20>tre sur la rang<6E>e devant le pion
if (Math.abs(x - enPassantTargetX) == 1) { // La cible doit <20>tre sur une colonne adjacente
// V<>rifier qu'il y a bien un pion adverse <20> capturer <20> c<>t<EFBFBD> de nous (sur la case EP virtuelle)
// Le pion <20> 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);

View File

@ -0,0 +1,123 @@
package backend;
import java.util.ArrayList;
import java.util.List;
public class MoveHistory {
private final List<MoveRecord> moveRecords;
private final Board board;
// Inner class to store a move along with board state (like en passant target)
private static class MoveRecord {
private final Move move;
private final int enPassantTargetX;
private final int enPassantTargetY;
public MoveRecord(Move move, int enPassantTargetX, int enPassantTargetY) {
this.move = move;
this.enPassantTargetX = enPassantTargetX;
this.enPassantTargetY = enPassantTargetY;
}
public Move getMove() {
return move;
}
public int getEnPassantTargetX() {
return enPassantTargetX;
}
public int getEnPassantTargetY() {
return enPassantTargetY;
}
}
public MoveHistory(Board board) {
this.board = board;
this.moveRecords = new ArrayList<>();
}
// Add a move to the history, capturing the board's state at this point
public void addMove(Move move) {
MoveRecord record = new MoveRecord(
move,
board.getEnPassantTargetX(),
board.getEnPassantTargetY()
);
moveRecords.add(record);
System.out.println("MoveHistory: Added move from (" + move.getFromX() + "," + move.getFromY() +
") to (" + move.getToX() + "," + move.getToY() + ")");
}
// Clear the move history (e.g., when resetting the board)
public void clear() {
moveRecords.clear();
System.out.println("MoveHistory: Cleared all moves");
}
// Undo the last move and update the board state
public boolean undoLastMove() {
if (moveRecords.isEmpty()) {
System.out.println("MoveHistory: No moves to undo");
return false;
}
MoveRecord lastRecord = moveRecords.remove(moveRecords.size() - 1);
Move lastMove = lastRecord.getMove();
System.out.println("MoveHistory: Undoing move from (" + lastMove.getFromX() + "," + lastMove.getFromY() +
") to (" + lastMove.getToX() + "," + lastMove.getToY() + ")");
// Restore the piece to its original position
Piece pieceMoved = board.getPieceAt(lastMove.getToX(), lastMove.getToY());
if (pieceMoved == null || pieceMoved.getType() != lastMove.getPieceMoved().getType() || pieceMoved.isWhite() != lastMove.getPieceMoved().isWhite()) {
System.err.println("MoveHistory: Piece mismatch during undo at (" + lastMove.getToX() + "," + lastMove.getToY() + ")");
return false;
}
pieceMoved.setPosition(lastMove.getFromX(), lastMove.getFromY());
// Restore any captured piece (normal capture or en passant)
Piece pieceCaptured = lastMove.getPieceCaptured();
boolean wasEnPassantCapture = false;
if (pieceCaptured != null) {
// Check if this was an en passant capture
if (lastMove.getToX() == lastRecord.getEnPassantTargetX() &&
lastMove.getToY() == lastRecord.getEnPassantTargetY() &&
lastMove.getPieceCaptured() == null) {
// For en passant, the captured pawn is at (toX, fromY)
int capturedPawnY = lastMove.getFromY();
board.setPiece(pieceCaptured.isWhite(), pieceCaptured.getType(), lastMove.getToX(), capturedPawnY);
System.out.println("MoveHistory: Restored en passant captured pawn at (" + lastMove.getToX() + "," + capturedPawnY + ")");
wasEnPassantCapture = true;
} else {
// Normal capture: restore the captured piece at the destination
board.setPiece(pieceCaptured.isWhite(), pieceCaptured.getType(), lastMove.getToX(), lastMove.getToY());
System.out.println("MoveHistory: Restored captured piece at (" + lastMove.getToX() + "," + lastMove.getToY() + ")");
}
}
// If this wasn't an en passant capture but the move captured a piece, remove the piece we just moved back
if (!wasEnPassantCapture && pieceCaptured != null) {
board.getPieces().removeIf(p -> p.getX() == lastMove.getToX() && p.getY() == lastMove.getToY() && p != pieceMoved);
}
// Restore the en passant target state
board.enPassantTargetX = lastRecord.getEnPassantTargetX();
board.enPassantTargetY = lastRecord.getEnPassantTargetY();
System.out.println("MoveHistory: Restored en passant target to (" + board.enPassantTargetX + "," + board.enPassantTargetY + ")");
// Update turn number and player turn
board.turnNumber--;
board.isTurnWhite = !board.isTurnWhite;
// Clear selection and highlights
board.selectedX = -1;
board.selectedY = -1;
board.clearHighlightedPositions();
System.out.println("MoveHistory: After undo - Pieces: " + board.getPieces().size());
for (Piece piece : board.getPieces()) {
System.out.println("Piece at (" + piece.getX() + "," + piece.getY() + "): " + piece.getType() + ", isWhite=" + piece.isWhite());
}
return true;
}
}