From 031b23c7c5031f1578bc3d6a93bdf15029cf9083 Mon Sep 17 00:00:00 2001 From: raban Date: Mon, 26 Jan 2026 13:41:48 +0100 Subject: [PATCH] Menu game mode + bot idiot --- fr/iut_fbleau/Avalam/AvalamWindow.java | 147 ++++++++++++++++++++----- fr/iut_fbleau/Avalam/BoardView.java | 78 ++++++++----- fr/iut_fbleau/Avalam/GameMode.java | 10 ++ fr/iut_fbleau/Avalam/Main.java | 62 +++++++---- fr/iut_fbleau/Bot/AlphaBetaBot.java | 22 ++++ fr/iut_fbleau/Bot/IdiotBot.java | 43 ++++++++ 6 files changed, 291 insertions(+), 71 deletions(-) create mode 100644 fr/iut_fbleau/Avalam/GameMode.java create mode 100644 fr/iut_fbleau/Bot/AlphaBetaBot.java create mode 100644 fr/iut_fbleau/Bot/IdiotBot.java diff --git a/fr/iut_fbleau/Avalam/AvalamWindow.java b/fr/iut_fbleau/Avalam/AvalamWindow.java index 36492e5..d0e6304 100644 --- a/fr/iut_fbleau/Avalam/AvalamWindow.java +++ b/fr/iut_fbleau/Avalam/AvalamWindow.java @@ -1,5 +1,7 @@ package fr.iut_fbleau.Avalam; +import fr.iut_fbleau.Bot.IdiotBot; +import fr.iut_fbleau.GameAPI.AbstractPly; import fr.iut_fbleau.GameAPI.Player; import fr.iut_fbleau.GameAPI.Result; @@ -12,10 +14,18 @@ import java.awt.*; * Fenêtre principale (interface graphique) du jeu Avalam. * Elle contient : * - le plateau (BoardView) -* - l'affichage du score (ScoreView) -* - l'affichage du joueur courant (TurnView) +* - 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 (préparé) +* +* @version 1.0 +* Date : +* Licence : */ public class AvalamWindow extends JFrame { @@ -24,36 +34,57 @@ public class AvalamWindow extends JFrame { /** Moteur du jeu (état + règles). */ private AvalamBoard board; - /** Vue affichant le score des deux couleurs. */ + /** Vue affichant le score. */ private ScoreView scoreView; - /** Vue affichant le joueur dont c'est le tour. */ + /** Vue affichant le joueur courant. */ private TurnView turnView; - /** Vue affichant le plateau et gérant les interactions de jeu. */ + /** 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; + + /** Indique si une animation de tour de bot est en cours. */ + private boolean botAnimating = false; + //Constructeur /** - * Construit la fenêtre et initialise l’interface : - * - charge le plateau initial depuis Plateau.txt - * - construit les vues (score, tour, plateau) - * - affiche la fenêtre + * Construit la fenêtre en mode joueur vs joueur. */ public AvalamWindow() { + this(GameMode.PVP); + } + + /** + * Construit la fenêtre en fonction du mode choisi. + * + * @param mode mode de jeu + */ + public AvalamWindow(GameMode mode) { super("Avalam"); + this.mode = mode; + this.idiotBot = (mode == GameMode.PVBOT) ? new IdiotBot(botPlayer) : null; + setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); - // Chargement du plateau initial depuis Plateau.txt + // Chargement du plateau initial Tower[][] initialGrid = BoardLoader.loadFromFile("fr/iut_fbleau/Res/Plateau.txt"); // Initialisation du moteur (PLAYER1 commence) board = new AvalamBoard(initialGrid); - // Création du panneau supérieur (score + tour) + // Panneau supérieur (score + tour) JPanel topPanel = new JPanel(new GridLayout(2, 1)); topPanel.setBackground(new java.awt.Color(200, 200, 200)); @@ -69,7 +100,7 @@ public class AvalamWindow extends JFrame { add(topPanel, BorderLayout.NORTH); - // Création de la vue plateau (avec callback de mise à jour) + // Plateau boardView = new BoardView(board, this::onBoardUpdated); add(boardView, BorderLayout.CENTER); @@ -77,35 +108,30 @@ public class AvalamWindow extends JFrame { setResizable(false); setLocationRelativeTo(null); setVisible(true); + + // Si un jour le bot doit commencer, on peut déclencher ici. + maybePlayBotTurn(); } //Méthodes /** - * Méthode appelée automatiquement après chaque coup (via BoardView). - * Elle rafraîchit : - * - les scores - * - le joueur courant - * et affiche un message si la partie est terminée. + * Appelée après chaque coup (humain ou bot). + * Met à jour score, tour, et affiche la fin de partie. */ public void onBoardUpdated() { - // Mise à jour du score scoreView.updateScores( computeScore(Color.YELLOW), computeScore(Color.RED) ); - // Mise à jour du joueur courant turnView.setTurn(turnMessage()); - // Détection de fin de partie if (board.isGameOver()) { Result res = board.getResult(); String msg; - - // Correction : ajout des "break" pour éviter le fall-through. switch (res) { case WIN: msg = "Le joueur jaune a gagné !"; @@ -127,11 +153,80 @@ public class AvalamWindow extends JFrame { "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(); } /** - * Calcule le score d'une couleur : nombre de tours contrôlées (sommet de la tour). + * Fait jouer le bot 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() { + + if (mode != GameMode.PVBOT) return; + if (board.isGameOver()) return; + if (board.getCurrentPlayer() != botPlayer) return; + if (botAnimating) 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 = idiotBot.giveYourMove(board.safeCopy()); + 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 @@ -150,12 +245,14 @@ public class AvalamWindow extends JFrame { } /** - * Retourne le message correspondant au joueur dont c'est le tour. + * Retourne le message affiché pour le joueur courant. * - * @return message d’affichage du tour + * @return message du tour */ private String turnMessage() { return "Tour du joueur : " + (board.getCurrentPlayer() == Player.PLAYER1 ? "Jaune" : "Rouge"); } + + //Affichage } diff --git a/fr/iut_fbleau/Avalam/BoardView.java b/fr/iut_fbleau/Avalam/BoardView.java index 208f79b..382c5e6 100644 --- a/fr/iut_fbleau/Avalam/BoardView.java +++ b/fr/iut_fbleau/Avalam/BoardView.java @@ -1,8 +1,5 @@ package fr.iut_fbleau.Avalam; -import fr.iut_fbleau.Avalam.AvalamBoard; -import fr.iut_fbleau.Avalam.Tower; - import javax.swing.*; import java.awt.*; @@ -14,33 +11,37 @@ import java.awt.*; * - l’affichage des tours (PieceLayer) * - l’affichage des coups possibles (HighlightLayer) * - l’affichage du fond graphique -* - la gestion des interactions via InteractionController +* - les clics via InteractionController * -* Cette classe ne contient aucune logique de jeu. +* Cette classe ne contient aucune logique de règles du jeu. +* +* @version 1.0 +* Date : +* Licence : */ public class BoardView extends JLayeredPane { //Attributs - /** Référence au moteur du jeu Avalam. */ + /** Référence au moteur Avalam. */ private AvalamBoard board; - /** Couche graphique du fond du plateau. */ + /** Couche d’affichage du fond. */ private BackgroundLayer backgroundLayer; - /** Couche graphique des déplacements possibles. */ + /** Couche d’affichage des coups possibles. */ private HighlightLayer highlightLayer; - /** Couche graphique des pièces (tours). */ + /** Couche d’affichage des pièces. */ private PieceLayer pieceLayer; - /** Contrôleur des interactions utilisateur. */ + /** Contrôleur des interactions. */ private InteractionController controller; /** Taille d’un pion en pixels. */ private final int size = 50; - /** Espacement entre deux cases du plateau. */ + /** Espacement entre les cases. */ private final int spacing = 70; /** Décalage horizontal du plateau. */ @@ -49,16 +50,16 @@ public class BoardView extends JLayeredPane { /** Décalage vertical du plateau. */ private final int yBase = 60; - /** Callback vers AvalamWindow pour mettre à jour l’interface (score, tour, fin). */ + /** Callback vers AvalamWindow pour mise à jour (score, tour, fin). */ private Runnable boardUpdateCallback; //Constructeur /** - * Construit la vue du plateau Avalam. + * Construit la vue du plateau. * - * @param board moteur du jeu Avalam - * @param boardUpdateCallback fonction de rappel après un coup + * @param board moteur du jeu + * @param boardUpdateCallback callback à appeler après un coup */ public BoardView(AvalamBoard board, Runnable boardUpdateCallback) { this.board = board; @@ -66,19 +67,19 @@ public class BoardView extends JLayeredPane { setLayout(null); - // --- Contrôleur --- + // Contrôleur this.controller = new InteractionController(board, this); - // --- Couche fond --- + // Couche fond backgroundLayer = new BackgroundLayer("fr/iut_fbleau/Res/BackgroundAvalam.png"); backgroundLayer.setBounds(0, 0, 725, 725); add(backgroundLayer, JLayeredPane.FRAME_CONTENT_LAYER); - // --- Couche highlight --- + // Couche highlight highlightLayer = new HighlightLayer(xBase, yBase, spacing, size); add(highlightLayer, JLayeredPane.DEFAULT_LAYER); - // --- Couche des pièces --- + // Couche pièces pieceLayer = new PieceLayer(); add(pieceLayer, JLayeredPane.PALETTE_LAYER); @@ -89,6 +90,29 @@ public class BoardView extends JLayeredPane { //Méthodes + /** + * Retourne le contrôleur d'interactions (utile pour simuler les clics du bot). + * + * @return contrôleur + */ + public InteractionController getController() { + return controller; + } + + /** + * Active/désactive les interactions utilisateur sur le plateau. + * Utile pendant l'animation du bot pour éviter des clics concurrents. + * + * @param enabled true pour activer, false pour désactiver + */ + public void setInteractionEnabled(boolean enabled) { + // Désactive la couche des pièces (boutons) principalement + pieceLayer.setEnabled(enabled); + for (Component c : pieceLayer.getComponents()) { + c.setEnabled(enabled); + } + } + /** * Appelé par le contrôleur après un coup. */ @@ -99,7 +123,7 @@ public class BoardView extends JLayeredPane { } /** - * Rafraîchit l’affichage du plateau. + * Rafraîchit les couches visuelles. */ public void refresh() { @@ -118,7 +142,7 @@ public class BoardView extends JLayeredPane { } /** - * Récupère la grille actuelle du moteur de jeu. + * Récupère la grille depuis le moteur. * * @return grille 9x9 de tours */ @@ -136,24 +160,26 @@ public class BoardView extends JLayeredPane { //Affichage /** - * Classe interne représentant la couche graphique du fond. + * Composant affichant l’image de fond. */ private static class BackgroundLayer extends JComponent { private Image img; /** - * Construit une couche de fond à partir d’une image. + * Construit une couche de fond. * - * @param resourcePath chemin de l’image + * @param resourcePath chemin de l'image de fond */ public BackgroundLayer(String resourcePath) { img = Toolkit.getDefaultToolkit().getImage( - getClass().getClassLoader().getResource(resourcePath) + getClass().getClassLoader().getResource(resourcePath) ); } /** - * Dessine l’image de fond. + * Dessine l'image de fond. + * + * @param g contexte graphique */ @Override protected void paintComponent(Graphics g) { diff --git a/fr/iut_fbleau/Avalam/GameMode.java b/fr/iut_fbleau/Avalam/GameMode.java new file mode 100644 index 0000000..8d5ac1b --- /dev/null +++ b/fr/iut_fbleau/Avalam/GameMode.java @@ -0,0 +1,10 @@ +package fr.iut_fbleau.Avalam; + +/** + * Mode de jeu au lancement. + */ +public enum GameMode { + PVP, // joueur vs joueur + PVBOT, // joueur vs bot idiot + PVALPHA // joueur vs bot alpha (préparé) +} diff --git a/fr/iut_fbleau/Avalam/Main.java b/fr/iut_fbleau/Avalam/Main.java index 4257f02..91bb0aa 100644 --- a/fr/iut_fbleau/Avalam/Main.java +++ b/fr/iut_fbleau/Avalam/Main.java @@ -1,28 +1,50 @@ package fr.iut_fbleau.Avalam; +import javax.swing.*; + /** -* La classe Main -* -* Point d’entrée du programme. -* Lance l’interface graphique principale (AvalamWindow). -*/ + * Point d’entrée : propose un menu de sélection de mode, puis lance la fenêtre Avalam. + */ public class Main { - //Attributs - - //Constructeur - public Main() { - - } - - //Méthodes - - /** - * Méthode principale : démarre l’application. - * - * @param args arguments de la ligne de commande (non utilisés) - */ public static void main(String[] args) { - new AvalamWindow(); + + SwingUtilities.invokeLater(() -> { + + String[] options = { + "joueur vs joueur", + "joueur vs botidiot", + "joueur vs bot alpha" + }; + + int choice = JOptionPane.showOptionDialog( + null, + "Choisissez un mode de jeu :", + "Avalam - Mode de jeu", + JOptionPane.DEFAULT_OPTION, + JOptionPane.QUESTION_MESSAGE, + null, + options, + options[0] + ); + + GameMode mode; + if (choice == 1) mode = GameMode.PVBOT; + else if (choice == 2) mode = GameMode.PVALPHA; + else mode = GameMode.PVP; + + // Si alpha choisi : non implémenté, on prévient et on lance en PVP (préparation). + if (mode == GameMode.PVALPHA) { + JOptionPane.showMessageDialog( + null, + "Bot Alpha-Beta non implémenté pour l'instant.\nLancement en joueur vs joueur.", + "Information", + JOptionPane.INFORMATION_MESSAGE + ); + mode = GameMode.PVP; + } + + new AvalamWindow(mode); + }); } } diff --git a/fr/iut_fbleau/Bot/AlphaBetaBot.java b/fr/iut_fbleau/Bot/AlphaBetaBot.java new file mode 100644 index 0000000..459b99f --- /dev/null +++ b/fr/iut_fbleau/Bot/AlphaBetaBot.java @@ -0,0 +1,22 @@ +package fr.iut_fbleau.Bot; + +import fr.iut_fbleau.GameAPI.AbstractGamePlayer; +import fr.iut_fbleau.GameAPI.AbstractPly; +import fr.iut_fbleau.GameAPI.IBoard; +import fr.iut_fbleau.GameAPI.Player; + +/** + * Bot Alpha-Beta (préparé). + * Pour l'instant non implémenté. + */ +public class AlphaBetaBot extends AbstractGamePlayer { + + public AlphaBetaBot(Player p) { + super(p); + } + + @Override + public AbstractPly giveYourMove(IBoard board) { + throw new UnsupportedOperationException("AlphaBetaBot non implémenté pour l'instant."); + } +} diff --git a/fr/iut_fbleau/Bot/IdiotBot.java b/fr/iut_fbleau/Bot/IdiotBot.java new file mode 100644 index 0000000..ffd5135 --- /dev/null +++ b/fr/iut_fbleau/Bot/IdiotBot.java @@ -0,0 +1,43 @@ +package fr.iut_fbleau.Bot; + +import fr.iut_fbleau.GameAPI.AbstractGamePlayer; +import fr.iut_fbleau.GameAPI.AbstractPly; +import fr.iut_fbleau.GameAPI.IBoard; +import fr.iut_fbleau.GameAPI.Player; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Random; + +/** + * Bot idiot : choisit un coup légal au hasard parmi ceux retournés par IBoard.iterator(). + * Compatible avec n'importe quel jeu respectant GameAPI (dont AvalamBoard). + */ +public class IdiotBot extends AbstractGamePlayer { + + private final Random rng; + + public IdiotBot(Player p) { + super(p); + this.rng = new Random(); + } + + @Override + public AbstractPly giveYourMove(IBoard board) { + + // Si la partie est terminée ou qu'il n'y a pas de coups, on ne joue rien. + if (board == null || board.isGameOver()) return null; + + Iterator it = board.iterator(); + List moves = new ArrayList<>(); + + while (it.hasNext()) { + moves.add(it.next()); + } + + if (moves.isEmpty()) return null; + + return moves.get(rng.nextInt(moves.size())); + } +}