package fr.iut_fbleau.Avalam; import fr.iut_fbleau.Bot.AlphaBetaBot; // A FAIRE PLUS TARD (PVGOD) // import fr.iut_fbleau.Bot.DivineBot; import fr.iut_fbleau.Bot.IdiotBot; import fr.iut_fbleau.GameAPI.AbstractPly; import fr.iut_fbleau.GameAPI.Player; import fr.iut_fbleau.GameAPI.Result; import javax.swing.*; import java.awt.*; /** * La classe AvalamWindow * * Fenêtre principale (interface graphique) du jeu Avalam. * Elle contient : * - le plateau (BoardView) * - l’affichage du score (ScoreView) * - l’affichage du joueur courant (TurnView) * * Elle pilote un objet AvalamBoard (moteur du jeu). * Elle peut fonctionner en mode : * - joueur vs joueur * - joueur vs bot idiot (aléatoire) * - joueur vs bot alpha (cut-off) * - joueur vs bot divin (PVGOD) * * @version 1.0 * Date : * Licence : */ public class AvalamWindow extends JFrame { //Attributs /** Moteur du jeu (état + règles). */ private AvalamBoard board; /** Vue affichant le score. */ private ScoreView scoreView; /** Vue affichant le joueur courant. */ private TurnView turnView; /** Vue affichant le plateau. */ private BoardView boardView; /** Mode de jeu sélectionné. */ private final GameMode mode; /** Joueur contrôlé par le bot (si actif). */ private final Player botPlayer = Player.PLAYER2; /** Bot idiot (utilisé si mode PVBOT). */ private final IdiotBot idiotBot; /** Bot Alpha-Beta (utilisé si mode PVALPHA). */ private final AlphaBetaBot alphaBot; // A FAIRE PLUS TARD (PVGOD) // /** Bot Divin (utilisé si mode PVGOD). */ // private final DivineBot divineBot; /** * A FAIRE PLUS TARD (PVGOD) * On garde l'attribut à null pour ne pas casser la compilation, * mais toute la logique PVGOD est désactivée/commentée. */ private final Object divineBot = null; /** Indique si une animation de tour de bot est en cours. */ private boolean botAnimating = false; //Constructeur /** * Construit la fenêtre en mode joueur vs joueur. */ public AvalamWindow() { this(GameMode.PVP, 4); } /** * Construit la fenêtre en fonction du mode choisi. * Pour PVALPHA/PVGOD, la profondeur par défaut est 4. * * @param mode mode de jeu */ public AvalamWindow(GameMode mode) { this(mode, 4); } /** * Construit la fenêtre en fonction du mode choisi. * Si le mode est PVALPHA ou PVGOD, la profondeur est utilisée comme cut-off. * * @param mode mode de jeu * @param alphaDepth profondeur de recherche pour Alpha-Beta / Bot Divin */ public AvalamWindow(GameMode mode, int alphaDepth) { super("Avalam"); this.mode = mode; this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null; int depth = Math.max(1, alphaDepth); this.alphaBot = (mode == GameMode.PVALPHA) ? new AlphaBetaBot(botPlayer, depth) : null; // A FAIRE PLUS TARD (PVGOD) // this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null; setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); // Chargement du plateau initial Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt"); // Initialisation du moteur (PLAYER1 commence) board = new AvalamBoard(initialGrid); // Panneau supérieur (score + tour) JPanel topPanel = new JPanel(new GridLayout(2, 1)); topPanel.setBackground(new java.awt.Color(200, 200, 200)); scoreView = new ScoreView( computeScore(Color.YELLOW), computeScore(Color.RED) ); turnView = new TurnView(turnMessage()); topPanel.add(scoreView); topPanel.add(turnView); add(topPanel, BorderLayout.NORTH); // Plateau boardView = new BoardView(board, this::onBoardUpdated); add(boardView, BorderLayout.CENTER); pack(); setResizable(false); setLocationRelativeTo(null); setVisible(true); // Si un jour le bot devait commencer (pas le cas ici), on le ferait jouer ici. maybePlayBotTurn(); } //Méthodes /** * Appelée après chaque coup (humain ou bot). * Met à jour score, tour, et affiche la fin de partie. */ public void onBoardUpdated() { scoreView.updateScores( computeScore(Color.YELLOW), computeScore(Color.RED) ); turnView.setTurn(turnMessage()); if (board.isGameOver()) { Result res = board.getResult(); String msg; switch (res) { case WIN: msg = "Le joueur jaune a gagné !"; break; case LOSS: msg = "Le joueur rouge a gagné !"; break; case DRAW: msg = "Égalité !"; break; default: msg = "Fin de partie."; break; } JOptionPane.showMessageDialog( this, msg, "Partie terminée", JOptionPane.INFORMATION_MESSAGE ); return; } // Si on est contre un bot et que c’est son tour, on déclenche son animation. maybePlayBotTurn(); } /** * Fait jouer le bot (idiot / alpha / divin) en deux étapes visibles : * 1) sélection de la tour (affiche les coups légaux) * 2) attente 1 seconde * 3) déplacement vers la destination * * Le tout sans bloquer l'interface (Timer Swing). */ private void maybePlayBotTurn() { // Mode joueur vs joueur : aucun bot if (mode == GameMode.PVP) return; // Sécurité if (board.isGameOver()) return; if (board.getCurrentPlayer() != botPlayer) return; if (botAnimating) return; // Vérifier qu'on a bien le bot correspondant if (mode == GameMode.PVBOT && idiotBot == null) return; if (mode == GameMode.PVALPHA && alphaBot == null) return; // A FAIRE PLUS TARD (PVGOD) // if (mode == GameMode.PVGOD && divineBot == null) return; // A FAIRE PLUS TARD (PVGOD) // Pour l'instant, si PVGOD est sélectionné, on ne joue pas de coup bot. if (mode == GameMode.PVGOD) return; botAnimating = true; // Désactiver les interactions du joueur pendant le tour du bot. boardView.setInteractionEnabled(false); // Choix d’un coup sur une copie sûre AbstractPly botMove; if (mode == GameMode.PVBOT) { botMove = idiotBot.giveYourMove(board.safeCopy()); } else if (mode == GameMode.PVALPHA) { botMove = alphaBot.giveYourMove(board.safeCopy()); } else { // A FAIRE PLUS TARD (PVGOD) // botMove = divineBot.giveYourMove(board.safeCopy()); botMove = null; } if (botMove == null) { botAnimating = false; boardView.setInteractionEnabled(true); return; } if (!(botMove instanceof AvalamPly)) { botAnimating = false; boardView.setInteractionEnabled(true); return; } AvalamPly ap = (AvalamPly) botMove; // Étape 1 : sélection (comme un clic humain) InteractionController ctrl = boardView.getController(); ctrl.onPieceClicked(ap.getXFrom(), ap.getYFrom()); boardView.refresh(); // Étape 2 : attendre puis cliquer la destination javax.swing.Timer t = new javax.swing.Timer(1000, e -> { // Sécurité : si la partie a changé entre temps if (board.isGameOver() || board.getCurrentPlayer() != botPlayer) { botAnimating = false; boardView.setInteractionEnabled(true); ((javax.swing.Timer) e.getSource()).stop(); return; } ctrl.onPieceClicked(ap.getXTo(), ap.getYTo()); boardView.refresh(); botAnimating = false; boardView.setInteractionEnabled(true); ((javax.swing.Timer) e.getSource()).stop(); }); t.setRepeats(false); t.start(); } /** * Calcule le score d'une couleur : nombre de tours contrôlées. * * @param c couleur à compter * @return nombre de tours appartenant à la couleur c */ private int computeScore(Color c) { int score = 0; for (int r = 0; r < AvalamBoard.SIZE; r++) { for (int col = 0; col < AvalamBoard.SIZE; col++) { Tower t = board.getTowerAt(r, col); if (t != null && t.getColor() == c) { score++; } } } return score; } /** * Retourne le message affiché pour le joueur courant. * * @return message du tour */ private String turnMessage() { return "Tour du joueur : " + (board.getCurrentPlayer() == Player.PLAYER1 ? "Jaune" : "Rouge"); } //Affichage }