package fr.iut_fbleau.Bot; import fr.iut_fbleau.Avalam.AvalamBoard; 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 java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Random; /** * Bot Alpha-Beta avec cut-off (profondeur maximale). * * Idée : * - si on atteint la profondeur limite, on évalue la position (heuristique). * - sinon, on explore les coups avec Alpha-Beta (minimax optimisé). * * */ public class AlphaBetaBot 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 des coups de même valeur. */ private final Random rng = new Random(); //Constructeur /** * Construit un bot Alpha-Beta. * * @param p joueur contrôlé par ce bot * @param maxDepth profondeur maximale de recherche */ public AlphaBetaBot(Player p, int maxDepth) { super(p); this.me = p; this.maxDepth = Math.max(1, maxDepth); } //Méthodes /** * Méthode appelée par GameAPI : le bot doit choisir un coup. * * @param board copie sûre de l'état de jeu (IBoard) * @return un coup (AbstractPly) ou null si aucun coup possible */ @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); 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); } } return bestMoves.get(rng.nextInt(bestMoves.size())); } /** * Fonction récursive 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); } boolean isMax = (board.getCurrentPlayer() == me); List moves = listMoves(board); if (moves.isEmpty()) { return evaluate(board); } if (isMax) { int best = Integer.MIN_VALUE; for (AbstractPly m : moves) { 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); if (alpha >= beta) break; } 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; } } /** * Convertit le résultat final en valeur très grande (victoire/défaite). * Result est du point de vue de PLAYER1. */ 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; } } /** * Heuristique simple Avalam : * score = (tours du bot) - (tours adverses) */ private int evaluate(IBoard board) { if (!(board instanceof AvalamBoard)) return 0; AvalamBoard b = (AvalamBoard) board; Color botColor = (me == Player.PLAYER1) ? Color.YELLOW : Color.RED; Color oppColor = (me == Player.PLAYER1) ? Color.RED : Color.YELLOW; int botTowers = 0; int oppTowers = 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; if (t.getColor() == botColor) botTowers++; else if (t.getColor() == oppColor) oppTowers++; } } return botTowers - oppTowers; } /** * Récupère tous les coups légaux via iterator(). */ private List listMoves(IBoard board) { List moves = new ArrayList<>(); Iterator it = board.iterator(); while (it.hasNext()) moves.add(it.next()); return moves; } //Affichage }