package fr.iut_fbleau.Bot; import fr.iut_fbleau.Avalam.*; import fr.iut_fbleau.GameAPI.*; import java.util.*; /** * 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); } // 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) { if (board == null || board.isGameOver()) return null; List moves = listMoves(board); if (moves.isEmpty()) return null; boolean isMax = board.getCurrentPlayer() == me; int bestValue = isMax ? Integer.MIN_VALUE : Integer.MAX_VALUE; List bestMoves = new ArrayList<>(); int alpha = Integer.MIN_VALUE; int beta = Integer.MAX_VALUE; for (AbstractPly m : moves) { 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) { if (value > bestValue) { bestValue = value; bestMoves.clear(); bestMoves.add(m); } else if (value == bestValue) { bestMoves.add(m); } alpha = Math.max(alpha, bestValue); } else { if (value < bestValue) { bestValue = value; bestMoves.clear(); bestMoves.add(m); } else if (value == bestValue) { bestMoves.add(m); } beta = Math.min(beta, bestValue); } } // Retourne un coup au hasard parmi les meilleurs ex-aequo return bestMoves.get(rng.nextInt(bestMoves.size())); } /** * Algorithme récursif de recherche avec élagage Alpha-Beta. */ private int alphaBeta(IBoard board, int depth, int alpha, int beta) { // 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; for (AbstractPly m : listMoves(board)) { IBoard next = board.safeCopy(); next.doPly(m); int val = alphaBeta(next, depth - 1, alpha, beta); 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 isMax ? alpha : beta; } /** * Calcule la valeur de l'état final (Victoire / Défaite). */ private int terminalValue(IBoard board) { Result r = board.getResult(); if (r == null) return 0; if (r == Result.DRAW) return 0; boolean botIsP1 = (me == Player.PLAYER1); // Si le bot gagne, valeur positive élevée, sinon valeur négative return ((r == Result.WIN) == botIsP1) ? 100000 : -100000; } /** * 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) { if (!(board instanceof AvalamBoard)) return 0; AvalamBoard b = (AvalamBoard) board; Color myColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED; Color oppColor = (myColor == Color.YELLOW) ? Color.RED : Color.YELLOW; 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) continue; int h = t.getHeight(); // 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 score -= value; } } return score; } /** * Génère la liste de tous les coups possibles sur le plateau donné. */ private List listMoves(IBoard board) { List moves = new ArrayList<>(); board.iterator().forEachRemaining(moves::add); return moves; } }