Files
BUT3ProjetJeuGroupe/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java

165 lines
5.4 KiB
Java
Raw Normal View History

2026-02-05 16:27:10 +01:00
package fr.iut_fbleau.HexGame;
import fr.iut_fbleau.GameAPI.*;
public class MiniMaxBot extends AbstractGamePlayer {
private int MAXDEPTH = 5;
2026-02-13 16:39:48 +01:00
/**
* En dessous (ou égal) à ce nombre de coups restants (cases vides),
* on considère que l'arbre est "petit" et on fait un alpha-bêta simple :
* pas de cut-off, on explore jusqu'aux positions terminales (isGameOver()).
*/
private int SMALL_TREE_MOVE_LIMIT = 6;
2026-02-05 16:27:10 +01:00
public MiniMaxBot(Player me) {
super(me); // Correct constructor usage
}
public Boolean jesuisMinimax(){
return true;
}
2026-02-05 16:27:10 +01:00
@Override
public AbstractPly giveYourMove(IBoard board) {
HexBoard hb = (HexBoard) board;
float bestScore = -Float.MAX_VALUE;
HexPly bestMove = null;
2026-02-13 16:39:48 +01:00
// Détermine si l'arbre est petit ou grand
int movesLeft = countLegalMoves(hb);
boolean useCutoff = (movesLeft > SMALL_TREE_MOVE_LIMIT);
int depthToUse = useCutoff ? MAXDEPTH : 0; // 0 car en "simple" on ne coupe pas sur depth
2026-02-05 16:27:10 +01:00
for (int i = 0; i < hb.getSize(); i++) {
for (int j = 0; j < hb.getSize(); j++) {
HexPly move = new HexPly(hb.getCurrentPlayer(), i, j);
if (hb.isLegal(move)) {
hb.doPly(move);
2026-02-13 16:39:48 +01:00
float score = minimax(hb, depthToUse, -Float.MAX_VALUE, Float.MAX_VALUE, true, useCutoff);
2026-02-05 16:27:10 +01:00
if (score > bestScore) {
bestScore = score;
bestMove = move;
}
hb.undoPly();
}
}
}
return bestMove;
}
2026-02-13 16:39:48 +01:00
/**
* Minimax + alpha-bêta
* - useCutoff = false : alpha-bêta "simple" -> on s'arrête uniquement sur isGameOver()
* - useCutoff = true : alpha-bêta avec cut-off -> arrêt si depth == 0 (heuristique)
*/
private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing, boolean useCutoff) {
// Toujours prioritaire : position terminale
if (board.isGameOver()) {
return terminalScore(board);
}
// Cut-off uniquement si demandé
if (useCutoff && depth == 0) {
2026-02-05 16:27:10 +01:00
return evaluateBoard(board);
}
if (isMaximizing) {
float bestScore = -Float.MAX_VALUE;
for (int i = 0; i < board.getSize(); i++) {
for (int j = 0; j < board.getSize(); j++) {
HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
if (board.isLegal(move)) {
board.doPly(move);
2026-02-13 16:39:48 +01:00
int nextDepth = useCutoff ? (depth - 1) : depth;
float score = minimax(board, nextDepth, alpha, beta, false, useCutoff);
2026-02-05 16:27:10 +01:00
bestScore = Math.max(bestScore, score);
alpha = Math.max(alpha, bestScore);
2026-02-13 16:39:48 +01:00
if (beta <= alpha) {
board.undoPly();
break; // Pruning
}
2026-02-05 16:27:10 +01:00
board.undoPly();
}
}
}
return bestScore;
} else {
float bestScore = Float.MAX_VALUE;
for (int i = 0; i < board.getSize(); i++) {
for (int j = 0; j < board.getSize(); j++) {
HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
if (board.isLegal(move)) {
board.doPly(move);
2026-02-13 16:39:48 +01:00
int nextDepth = useCutoff ? (depth - 1) : depth;
float score = minimax(board, nextDepth, alpha, beta, true, useCutoff);
2026-02-05 16:27:10 +01:00
bestScore = Math.min(bestScore, score);
beta = Math.min(beta, bestScore);
2026-02-13 16:39:48 +01:00
if (beta <= alpha) {
board.undoPly();
break; // Pruning
}
2026-02-05 16:27:10 +01:00
board.undoPly();
}
}
}
return bestScore;
}
}
2026-02-13 16:39:48 +01:00
/**
* Score terminal (feuille) du point de vue de PLAYER1 :
* - WIN : PLAYER1 gagne
* - LOSS : PLAYER1 perd
*/
private float terminalScore(HexBoard board) {
Result r = board.getResult();
if (r == null) return 0f;
if (r == Result.WIN) return 1000000f;
return -1000000f;
}
/**
* Heuristique actuelle (inchangée) : distance au centre des pions PLAYER1.
* (Je ne la modifie pas pour ne pas toucher à la logique existante.)
*/
2026-02-05 16:27:10 +01:00
private float evaluateBoard(HexBoard board) {
int size = board.getSize();
int center = size / 2;
int score = 0;
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
2026-02-06 11:00:03 +01:00
if (board.getCellPlayer(i, j) == Player.PLAYER1) {
2026-02-05 16:27:10 +01:00
score += Math.abs(i - center) + Math.abs(j - center); // Distance from center
}
}
}
return score;
}
2026-02-13 16:39:48 +01:00
/**
* Compte les coups légaux (cases vides) sur le plateau courant.
*/
private int countLegalMoves(HexBoard board) {
int count = 0;
for (int i = 0; i < board.getSize(); i++) {
for (int j = 0; j < board.getSize(); j++) {
if (board.getCellPlayer(i, j) == null) {
count++;
}
}
}
return count;
}
}