From 2133960585d0ead2f12a515d5320720522fd8e4b Mon Sep 17 00:00:00 2001 From: cleme Date: Wed, 23 Apr 2025 12:49:21 +0200 Subject: [PATCH] remove static methods --- src/backend/Board.java | 5 +- src/backend/Game.java | 233 ++++++++++------------------------------- src/backend/Move.java | 101 ++++++++++++------ src/backend/Piece.java | 8 +- 4 files changed, 133 insertions(+), 214 deletions(-) diff --git a/src/backend/Board.java b/src/backend/Board.java index c81f784..2fa74bd 100644 --- a/src/backend/Board.java +++ b/src/backend/Board.java @@ -97,7 +97,10 @@ public class Board { private void setHighlightedPositions(Piece piece) { this.highlightedPositions.clear(); if (piece != null) { - Set validMoves = Move.calculateValidMoves(this, piece); + // Create a temporary Move object to call calculateValidMoves + // We can use dummy coordinates and null for pieceCaptured since they aren't used in calculateValidMoves + Move tempMove = new Move(0, 0, 0, 0, piece, null); + Set validMoves = tempMove.calculateValidMoves(this, piece); this.highlightedPositions.addAll(validMoves); } } diff --git a/src/backend/Game.java b/src/backend/Game.java index 45d67ed..d5ca151 100644 --- a/src/backend/Game.java +++ b/src/backend/Game.java @@ -1,35 +1,22 @@ package backend; -import windowInterface.MyInterface; // Assurez-vous que ce chemin d'importation est correct +import windowInterface.MyInterface; + -/** - * 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 { - // 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 Board board; + private final AutoPlayer aiPlayer; + private final MyInterface mjf; - // 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; - // État de contrôle du jeu - private int loopDelay; // Délai actuel de la boucle (peut être modifié) + private int loopDelay; 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) { if (mjfParam == null) { throw new IllegalArgumentException("MyInterface parameter cannot be null."); @@ -49,197 +36,131 @@ public class Game extends Thread { 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() { + public int getHeight() { return board.getHeight(); } - /** - * @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); } - /** @return true si c'est le tour des blancs, false si c'est le tour des noirs. */ + //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 + while (!Thread.currentThread().isInterrupted()) { 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 + Thread.currentThread().interrupt(); } 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 + + Board boardCopy = new Board(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) { 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; @@ -249,10 +170,7 @@ 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. - */ + public void setDefaultSetup() { System.out.println("Setting default board setup..."); board.cleanBoard(); // Nettoie le plateau (pièces, sélection, highlights, tour) @@ -263,90 +181,51 @@ public class Game extends Thread { 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 + // TODO: + boolean success = board.undoLastMove(); 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()); } 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()); } diff --git a/src/backend/Move.java b/src/backend/Move.java index 0eeacb5..49763f4 100644 --- a/src/backend/Move.java +++ b/src/backend/Move.java @@ -17,7 +17,7 @@ public class Move { this.toX = toX; this.toY = toY; if (pieceMoved == null) { - throw new IllegalArgumentException("pieceMoved cannot be null in a Move."); + throw new IllegalArgumentException("pieceMoved cannot be null in a Move."); } this.pieceMoved = pieceMoved; this.pieceCaptured = pieceCaptured; @@ -30,7 +30,7 @@ public class Move { public Piece getPieceMoved() { return pieceMoved; } public Piece getPieceCaptured() { return pieceCaptured; } - public static Set calculateValidMoves(Board board, Piece piece) { + public Set calculateValidMoves(Board board, Piece piece) { Set validMoves = new HashSet<>(); if (piece == null || board == null) { @@ -64,19 +64,19 @@ public class Move { return validMoves; } - private static void addMove(int x, int y, Set moves) { + private void addMove(int x, int y, Set moves) { moves.add(x + "," + y); } - static boolean isValidPosition(Board board, int x, int y) { + boolean isValidPosition(Board board, int x, int y) { return x >= 0 && x < board.getWidth() && y >= 0 && y < board.getHeight(); } - private static Piece getPieceAt(Board board, int x, int y) { - return board.getPieceAt(x, y); + private Piece getPieceAt(Board board, int x, int y) { + return board.getPieceAt(x, y); } - private static void calculateSlidingMoves(Board board, Piece piece, int x, int y, Set validMoves, int[][] directions) { + private 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]; @@ -102,7 +102,7 @@ public class Move { } } - private static void calculatePawnMoves(Board board, Piece piece, int x, int y, Set validMoves) { + private void calculatePawnMoves(Board board, Piece piece, int x, int y, Set validMoves) { int forwardDirection = piece.isWhite() ? -1 : 1; int startRow = piece.isWhite() ? 6 : 1; @@ -129,27 +129,27 @@ public class Move { } } - // V<>rifier la capture 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 <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 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()) { + // 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 - } + } } } } } - private static void calculateKnightMoves(Board board, Piece piece, int x, int y, Set validMoves) { + private 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} @@ -166,7 +166,7 @@ public class Move { } } - private static void calculateKingMoves(Board board, Piece piece, int x, int y, Set validMoves) { + private 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; @@ -183,42 +183,77 @@ public class Move { } } // Ajouter calcul du roque ici - calculateCastlingMoves(board, piece, x, y, validMoves); + calculateCastlingMoves1(board, piece, x, y, validMoves); } - private static void calculateCastlingMoves(Board board, Piece piece, int x, int y, Set validMoves) { + private void calculateCastlingMoves1(Board board, Piece piece, int x, int y, Set validMoves) { if (piece.getType() != PieceType.King || piece.hasMoved()) { return; } - // Roque c<>t roi + // Roque côté roi if (canCastleKingside(board, piece, x, y)) { addMove(x + 2, y, validMoves); } - // Roque c<>t dame + // Roque côté dame if (canCastleQueenside(board, piece, x, y)) { addMove(x - 2, y, validMoves); } } - private static boolean canCastleKingside(Board board, Piece king, int x, int y) { + private void calculateCastlingMoves(Board board, Piece piece, int x, int y, Set validMoves) { + // Castling is only possible for a king + if (piece.getType() != PieceType.King) { + return; + } + + // The king must not have moved + if (piece.hasMoved()) { + return; + } + + // Roque côté roi (kingside castling) + if (canCastleKingside(board, piece, x, y)) { + addMove(x + 2, y, validMoves); + } + + // Roque côté dame (queenside castling) + if (canCastleQueenside(board, piece, x, y)) { + addMove(x - 2, y, validMoves); + } + } + + private boolean canCastleKingside(Board board, Piece king, int x, int y) { Piece rook = board.getPieceAt(x + 3, y); - return rook != null && rook.getType() == PieceType.Rook && !rook.hasMoved() && - board.getPieceAt(x + 1, y) == null && board.getPieceAt(x + 2, y) == null && + // The rook must exist, be a rook, and not have moved + // The squares between the king and rook must be empty + // The king must not be in check + return rook != null && + rook.getType() == PieceType.Rook && + !rook.hasMoved() && + board.getPieceAt(x + 1, y) == null && + board.getPieceAt(x + 2, y) == null && !isKingInCheck(board, king.isWhite()); } - private static boolean canCastleQueenside(Board board, Piece king, int x, int y) { + private boolean canCastleQueenside(Board board, Piece king, int x, int y) { Piece rook = board.getPieceAt(x - 4, y); - return rook != null && rook.getType() == PieceType.Rook && !rook.hasMoved() && - board.getPieceAt(x - 1, y) == null && board.getPieceAt(x - 2, y) == null && board.getPieceAt(x - 3, y) == null && + // The rook must exist, be a rook, and not have moved + // The squares between the king and rook must be empty + // The king must not be in check + return rook != null && + rook.getType() == PieceType.Rook && + !rook.hasMoved() && + board.getPieceAt(x - 1, y) == null && + board.getPieceAt(x - 2, y) == null && + board.getPieceAt(x - 3, y) == null && !isKingInCheck(board, king.isWhite()); } - private static boolean isKingInCheck(Board board, boolean isWhite) { - // Impl<70>mentez la logique pour v<>rifier si le roi est en <20>chec - // Cela n<>cessite de v<>rifier tous les mouvements possibles des pi<70>ces adverses + private boolean isKingInCheck(Board board, boolean isWhite) { + // Implémentez la logique pour vérifier si le roi est en échec + // Cela nécessite de vérifier tous les mouvements possibles des pièces adverses return false; } @@ -230,4 +265,4 @@ public class Move { "-" + captureStr + "(" + toX + "," + toY + ")"; } -} +} \ No newline at end of file diff --git a/src/backend/Piece.java b/src/backend/Piece.java index acc95e0..e2e9bef 100644 --- a/src/backend/Piece.java +++ b/src/backend/Piece.java @@ -38,12 +38,14 @@ public class Piece { public boolean canMoveTo(int toX, int toY, Board board) { if (board == null) return false; if (this.x == toX && this.y == toY) return false; - if (!Move.isValidPosition(board, toX, toY)) return false; + // Create a temporary Move object to call isValidPosition + Move tempMove = new Move(this.x, this.y, toX, toY, this, null); + if (!tempMove.isValidPosition(board, toX, toY)) return false; Piece pieceAtDestination = board.getPieceAt(toX, toY); if (pieceAtDestination != null && pieceAtDestination.isWhite() == this.isWhite) { - return false; + return false; } int dx = Math.abs(toX - this.x); @@ -63,7 +65,7 @@ public class Piece { case King: return dx <= 1 && dy <= 1; default: - System.err.println("Erreur: Type de pi<70>ce inconnu dans canMoveTo: " + this.type); + System.err.println("Erreur: Type de pièce inconnu dans canMoveTo: " + this.type); return false; } }