From 32f77e549577f963c093f30611d3f675f6fec409 Mon Sep 17 00:00:00 2001 From: raban Date: Fri, 30 Jan 2026 14:11:13 +0100 Subject: [PATCH] =?UTF-8?q?Bot=20Divin=20et=20AvalamWindow=20connect=C3=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- fr/iut_fbleau/Avalam/AvalamWindow.java | 13 +-- fr/iut_fbleau/Bot/DivineBot.java | 155 ++++++++++++------------- 2 files changed, 80 insertions(+), 88 deletions(-) diff --git a/fr/iut_fbleau/Avalam/AvalamWindow.java b/fr/iut_fbleau/Avalam/AvalamWindow.java index ddec50e..1497478 100644 --- a/fr/iut_fbleau/Avalam/AvalamWindow.java +++ b/fr/iut_fbleau/Avalam/AvalamWindow.java @@ -2,7 +2,7 @@ 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.DivineBot; import fr.iut_fbleau.Bot.IdiotBot; import fr.iut_fbleau.GameAPI.AbstractPly; import fr.iut_fbleau.GameAPI.Player; @@ -59,6 +59,8 @@ public class AvalamWindow extends JFrame { /** Bot Alpha-Beta (utilisé si mode PVALPHA). */ private final AlphaBetaBot alphaBot; + private final DivineBot divineBot; + // A FAIRE PLUS TARD (PVGOD) // /** Bot Divin (utilisé si mode PVGOD). */ // private final DivineBot divineBot; @@ -68,7 +70,6 @@ public class AvalamWindow extends JFrame { * 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; @@ -110,7 +111,7 @@ public class AvalamWindow extends JFrame { 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; + this.divineBot = (mode == GameMode.PVGOD) ? new DivineBot(botPlayer, depth) : null; setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setLayout(new BorderLayout()); @@ -220,11 +221,10 @@ public class AvalamWindow extends JFrame { if (mode == GameMode.PVALPHA && alphaBot == null) return; // A FAIRE PLUS TARD (PVGOD) - // if (mode == GameMode.PVGOD && divineBot == null) return; + 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; @@ -239,8 +239,7 @@ public class AvalamWindow extends JFrame { botMove = alphaBot.giveYourMove(board.safeCopy()); } else { // A FAIRE PLUS TARD (PVGOD) - // botMove = divineBot.giveYourMove(board.safeCopy()); - botMove = null; + botMove = divineBot.giveYourMove(board.safeCopy()); } if (botMove == null) { diff --git a/fr/iut_fbleau/Bot/DivineBot.java b/fr/iut_fbleau/Bot/DivineBot.java index 3ffd569..0361e98 100644 --- a/fr/iut_fbleau/Bot/DivineBot.java +++ b/fr/iut_fbleau/Bot/DivineBot.java @@ -1,35 +1,51 @@ package fr.iut_fbleau.Bot; -import fr.iut_fbleau.Avalam.AvalamBoard; -import fr.iut_fbleau.Avalam.AvalamPly; -import fr.iut_fbleau.Avalam.Color; -import fr.iut_fbleau.Avalam.Tower; -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 fr.iut_fbleau.GameAPI.Result; +import fr.iut_fbleau.Avalam.*; +import fr.iut_fbleau.GameAPI.*; import java.util.*; /** - * Bot "Divin" (fort) pour Avalam. - * - * - * Objectif : trop fort. */ -public class DivineBot{ + * Bot "Divin" (alpha-beta + évaluateur pondéré). + * * Idée : + * - Utilise l'algorithme Alpha-Beta pour anticiper les coups. + * - Évalue les plateaux non terminaux en accordant plus d'importance aux tours hautes. + */ +public class DivineBot extends AbstractGamePlayer { + + // Attributs + + /** Joueur contrôlé par ce bot (PLAYER1 ou PLAYER2). */ private final Player me; + + /** Profondeur maximale de recherche avant évaluation. */ private final int maxDepth; + + /** Générateur aléatoire pour choisir parmi les meilleurs coups équivalents. */ private final Random rng = new Random(); + // Constructeur + + /** + * Construit le bot Divine. + * + * @param p joueur contrôlé par ce bot + * @param maxDepth profondeur de l'arbre de recherche + */ public DivineBot(Player p, int maxDepth) { super(p); this.me = p; this.maxDepth = Math.max(1, maxDepth); } - // ===================== COUP À JOUER ===================== + // Méthodes + /** + * Méthode principale de décision du bot. + * Explore le premier niveau de l'arbre et lance les appels Alpha-Beta. + * * @param board état actuel du jeu + * @return le meilleur coup calculé (AbstractPly) + */ @Override public AbstractPly giveYourMove(IBoard board) { @@ -50,6 +66,7 @@ public class DivineBot{ IBoard next = board.safeCopy(); next.doPly(m); + // Appel récursif pour évaluer la suite du coup int value = alphaBeta(next, maxDepth - 1, alpha, beta); if (isMax) { @@ -73,80 +90,57 @@ public class DivineBot{ } } + // Retourne un coup au hasard parmi les meilleurs ex-aequo return bestMoves.get(rng.nextInt(bestMoves.size())); } - // ===================== ALPHA-BETA ===================== - + /** + * Algorithme récursif de recherche avec élagage Alpha-Beta. + */ private int alphaBeta(IBoard board, int depth, int alpha, int beta) { - if (board.isGameOver()) { - return terminalValue(board); - } - - if (depth == 0) { - return evaluate(board); - } - - List moves = listMoves(board); - if (moves.isEmpty()) { - return evaluate(board); - } + // Cas de base : fin de partie ou limite de profondeur atteinte + if (board.isGameOver()) return terminalValue(board); + if (depth == 0) return evaluate(board); boolean isMax = board.getCurrentPlayer() == me; - if (isMax) { - int best = Integer.MIN_VALUE; - for (AbstractPly m : moves) { - IBoard next = board.safeCopy(); - next.doPly(m); + for (AbstractPly m : listMoves(board)) { + IBoard next = board.safeCopy(); + next.doPly(m); - int val = alphaBeta(next, depth - 1, alpha, beta); - best = Math.max(best, val); - alpha = Math.max(alpha, best); + int val = alphaBeta(next, depth - 1, alpha, beta); - if (alpha >= beta) break; + if (isMax) { + alpha = Math.max(alpha, val); + if (alpha >= beta) break; // Coupure Beta + } else { + beta = Math.min(beta, val); + if (alpha >= beta) break; // Coupure Alpha } - return best; - } else { - int best = Integer.MAX_VALUE; - for (AbstractPly m : moves) { - IBoard next = board.safeCopy(); - next.doPly(m); - - int val = alphaBeta(next, depth - 1, alpha, beta); - best = Math.min(best, val); - beta = Math.min(beta, best); - - if (alpha >= beta) break; - } - return best; } + + return isMax ? alpha : beta; } - // ===================== TERMINAL ===================== - + /** + * Calcule la valeur de l'état final (Victoire / Défaite). + */ private int terminalValue(IBoard board) { Result r = board.getResult(); if (r == null) return 0; - boolean botIsP1 = (me == Player.PLAYER1); - if (r == Result.DRAW) return 0; - if (botIsP1) { - return (r == Result.WIN) ? 100000 : -100000; - } else { - return (r == Result.LOSS) ? 100000 : -100000; - } + boolean botIsP1 = (me == Player.PLAYER1); + // Si le bot gagne, valeur positive élevée, sinon valeur négative + return ((r == Result.WIN) == botIsP1) ? 100000 : -100000; } - // ===================== ÉVALUATEUR ===================== - /** - * Évaluateur heuristique Avalam : - * - tours finales > quasi finales > stables > faibles - * - approximation du score final + * Heuristique évoluée pour Avalam : + * Calcule un score basé sur le contrôle des tours et leur hauteur. + * Les tours de hauteur 5 sont prioritaires car elles sont bloquées. */ private int evaluate(IBoard board) { @@ -154,7 +148,7 @@ public class DivineBot{ AvalamBoard b = (AvalamBoard) board; Color myColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED; - Color oppColor = (me == Player.PLAYER1) ? Color.RED : Color.YELLOW; + Color oppColor = (myColor == Color.YELLOW) ? Color.RED : Color.YELLOW; int score = 0; @@ -165,28 +159,27 @@ public class DivineBot{ if (t == null) continue; int h = t.getHeight(); - int value; - - if (h == 5) value = 1000; // tour gagnée - else if (h == 4) value = 300; // quasi gagnée - else if (h == 3) value = 120; // stable - else if (h == 2) value = 40; - else value = 10; + + // Pondération selon la hauteur (heuristique "Divine") + int value = + (h == 5) ? 1000 : + (h == 4) ? 300 : + (h == 3) ? 120 : + (h == 2) ? 40 : 10; if (t.getColor() == myColor) score += value; - else if (t.getColor() == oppColor) score -= value; + else score -= value; } } - return score; } - // ===================== OUTILS ===================== - + /** + * Génère la liste de tous les coups possibles sur le plateau donné. + */ private List listMoves(IBoard board) { List moves = new ArrayList<>(); - Iterator it = board.iterator(); - while (it.hasNext()) moves.add(it.next()); + board.iterator().forEachRemaining(moves::add); return moves; } -} +} \ No newline at end of file