package fr.iut_fbleau.Bot; import fr.iut_fbleau.Avalam.*; import fr.iut_fbleau.GameAPI.*; import java.util.*; /** * Bot expert utilisant l'algorithme Alpha-Beta pour le jeu Avalam. * * Idée : * - Explore l'arbre des coups possibles jusqu'à une profondeur donnée. * - Utilise l'élagage Alpha-Beta pour optimiser la recherche. * - Évalue les positions selon le contrôle des tours et leur hauteur. */ public class DivineBot extends AbstractGamePlayer { // Attributs /** Joueur contrôlé par ce bot (PLAYER1 ou PLAYER2). */ private final Player me; /** Profondeur maximale de recherche (cut-off). */ private final int maxDepth; /** Générateur aléatoire pour départager les coups de même valeur. */ private final Random rng = new Random(); // Constructeur /** * Construit un bot DivineBot. * * @param p joueur contrôlé par ce bot * @param maxDepth profondeur maximale de recherche */ public DivineBot(Player p, int maxDepth) { super(p); this.me = p; this.maxDepth = maxDepth; } // Méthodes /** * Méthode appelée par GameAPI : le bot doit choisir le meilleur coup possible. * * @param board copie sûre de l'état de jeu (IBoard) * @return le coup choisi (AbstractPly) ou null si aucun coup n'est possible */ @Override public AbstractPly giveYourMove(IBoard board) { if (board == null || board.isGameOver()) return null; List moves = listMoves(board); int bestValue = Integer.MIN_VALUE; List bestMoves = new ArrayList<>(); for (AbstractPly m : moves) { IBoard next = board.safeCopy(); next.doPly(m); // On calcule la valeur du plateau après ce coup int value = alphaBeta(next, maxDepth - 1, Integer.MIN_VALUE, Integer.MAX_VALUE); if (value > bestValue) { bestValue = value; bestMoves.clear(); bestMoves.add(m); } else if (value == bestValue) { bestMoves.add(m); } } return bestMoves.get(rng.nextInt(bestMoves.size())); } /** * Fonction récursive Alpha-Beta pour évaluer l'arbre de décision. * * @param board état actuel du plateau * @param depth profondeur restante à explorer * @param alpha borne inférieure * @param beta borne supérieure * @return la valeur de l'évaluation */ private int alphaBeta(IBoard board, int depth, int alpha, int beta) { if (board.isGameOver()) { Result r = board.getResult(); if (r == Result.DRAW) return 0; boolean p1Wins = (r == Result.WIN); boolean amIP1 = (me == Player.PLAYER1); return (p1Wins == amIP1) ? 10000 : -10000; } if (depth == 0) return evaluate(board); // Si c'est à moi de jouer, je maximise. Sinon, je minimise. boolean isMax = (board.getCurrentPlayer() == me); int best = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE; for (AbstractPly m : listMoves(board)) { IBoard next = board.safeCopy(); next.doPly(m); int val = alphaBeta(next, depth - 1, alpha, beta); if (isMax) { best = Math.max(best, val); alpha = Math.max(alpha, best); } else { best = Math.min(best, val); beta = Math.min(beta, best); } if (alpha >= beta) break; } return best; } /** * Heuristique spécifique à Avalam. * Valorise le contrôle des tours, avec un bonus pour les tours de hauteur 5 (verrouillées). * * @param board plateau à évaluer * @return score numérique de la position (positif si avantageux) */ private int evaluate(IBoard board) { if (!(board instanceof AvalamBoard)) return 0; AvalamBoard b = (AvalamBoard) board; // Configuration des couleurs Color myColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED; int score = 0; for (int r = 0; r < AvalamBoard.SIZE; r++) { for (int c = 0; c < AvalamBoard.SIZE; c++) { Tower t = b.getTowerAt(r, c); if (t == null || t.getHeight() == 0) continue; // Une tour de 5 vaut beaucoup plus car elle est "verrouillée". int val = (t.getHeight() == 5) ? 10 : 1; if (t.getColor() == myColor) { score += val; } else { score -= val; } } } return score; } /** * Récupère la liste de tous les coups légaux disponibles sur le plateau. * * @param board plateau actuel * @return liste des coups possibles */ private List listMoves(IBoard board) { List moves = new ArrayList<>(); Iterator it = board.iterator(); while (it.hasNext()) { moves.add(it.next()); } return moves; } }