diff --git a/build/fr/iut_fbleau/HexGame/Arena.class b/build/fr/iut_fbleau/HexGame/Arena.class
new file mode 100644
index 0000000..3f8ddca
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/Arena.class differ
diff --git a/build/fr/iut_fbleau/HexGame/ArenaMain.class b/build/fr/iut_fbleau/HexGame/ArenaMain.class
new file mode 100644
index 0000000..18210ec
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/ArenaMain.class differ
diff --git a/javaAPI/fr/iut_fbleau.tar b/javaAPI/fr/iut_fbleau.tar
index 9094711..87874e0 100644
Binary files a/javaAPI/fr/iut_fbleau.tar and b/javaAPI/fr/iut_fbleau.tar differ
diff --git a/javaAPI/fr/iut_fbleau/HexGame/Arena.java b/javaAPI/fr/iut_fbleau/HexGame/Arena.java
new file mode 100644
index 0000000..7ec4064
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/Arena.java
@@ -0,0 +1,62 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.EnumMap;
+import java.util.List;
+
+public class Arena {
+
+ private List bots = new ArrayList<>();
+ private FileWriter csvWriter;
+
+ public Arena() {
+ try {
+ csvWriter = new FileWriter("arena_results.csv");
+ csvWriter.append("Bot 1, Bot 2, Winner\n");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void addBot(AbstractGamePlayer bot) {
+ bots.add(bot);
+ }
+
+ public void run() {
+ for (int i = 0; i < bots.size(); i++) {
+ for (int j = i + 1; j < bots.size(); j++) {
+ AbstractGamePlayer bot1 = bots.get(i);
+ AbstractGamePlayer bot2 = bots.get(j);
+
+ System.out.println("Running match: " + bot1.getClass().getSimpleName() + " vs " + bot2.getClass().getSimpleName());
+ Result result = playMatch(bot1, bot2);
+
+ try {
+ csvWriter.append(bot1.getClass().getSimpleName() + "," + bot2.getClass().getSimpleName() + "," + result + "\n");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ try {
+ csvWriter.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ private Result playMatch(AbstractGamePlayer bot1, AbstractGamePlayer bot2) {
+ IBoard board = new HexBoard(11); // Standard 11x11 Hex board
+ EnumMap players = new EnumMap<>(Player.class);
+ players.put(Player.PLAYER1, bot1);
+ players.put(Player.PLAYER2, bot2);
+
+ Simulation simulation = new Simulation(board, players); // Ensure Simulation is correctly imported
+ return simulation.run();
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java
new file mode 100644
index 0000000..5dac4fe
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java
@@ -0,0 +1,15 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.Player;
+
+public class ArenaMain {
+ public static void main(String[] args) {
+ Arena arena = new Arena();
+ arena.addBot(new RandomBot(Player.PLAYER1, 12345L)); // Correct constructor usage
+ arena.addBot(new MiniMaxBot(Player.PLAYER2));
+ arena.addBot(new HeuristicBot(Player.PLAYER1));
+ arena.addBot(new MonteCarloBot(Player.PLAYER2)); // Correct constructor usage
+
+ arena.run();
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java b/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java
new file mode 100644
index 0000000..1666346
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java
@@ -0,0 +1,48 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+public class HeuristicBot extends AbstractGamePlayer {
+
+ public HeuristicBot(Player me) {
+ super(me); // Correct constructor usage
+ }
+
+ @Override
+ public AbstractPly giveYourMove(IBoard board) {
+ HexBoard hb = (HexBoard) board;
+ float bestScore = -Float.MAX_VALUE;
+ HexPly bestMove = null;
+
+ 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);
+ float score = evaluateBoard(hb);
+ if (score > bestScore) {
+ bestScore = score;
+ bestMove = move;
+ }
+ hb.undoPly();
+ }
+ }
+ }
+ return bestMove;
+ }
+
+ private float evaluateBoard(HexBoard board) {
+ int size = board.getSize();
+ int center = size / 2;
+ float score = 0;
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (board.getPlayerAt(i, j) == Player.PLAYER1) {
+ score += Math.abs(i - center) + Math.abs(j - center); // Distance from center
+ }
+ }
+ }
+ return score;
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
index c90002d..d314aa7 100644
--- a/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
@@ -15,6 +15,19 @@ import java.util.Random;
* java fr.iut_fbleau.HexGame.HexSimMain
* java fr.iut_fbleau.HexGame.HexSimMain --games 10000 --size 7 --seed 123
* java fr.iut_fbleau.HexGame.HexSimMain --games 5000 --size 11 --csv results.csv
+ *
+ * À seed identique, la suite de nombres
+ * pseudo-aléatoires générée est identique, donc les bots "aléatoires" joueront les mêmes coups
+ * dans le même ordre (tant que le code et l'ordre des appels à Random ne changent pas).
+ *
+ * Intérêt :
+ *
+ * Reproductibilité : relancer exactement la même simulation pour déboguer / analyser.
+ * Comparaison équitable : comparer 2 bots sur les mêmes tirages aléatoires.
+ * Si aucun seed n'est fourni, on utilise généralement l'heure courante, ce qui rend chaque exécution différente.
+ *
+ * long seed;
+ *
*/
public class HexSimMain {
@@ -170,7 +183,7 @@ public class HexSimMain {
// ex: "7 10000"
if (isInt(s)) {
int v = Integer.parseInt(s);
- if (a.size == 7) a.size = v;
+ if (a.size == 11) a.size = v;
else a.games = v;
} else {
System.err.println("Unknown arg: " + s);
diff --git a/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java
new file mode 100644
index 0000000..bb71baf
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java
@@ -0,0 +1,89 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+public class MiniMaxBot extends AbstractGamePlayer {
+
+ private int MAXDEPTH = 5;
+
+ public MiniMaxBot(Player me) {
+ super(me); // Correct constructor usage
+ }
+
+ @Override
+ public AbstractPly giveYourMove(IBoard board) {
+ HexBoard hb = (HexBoard) board;
+ float bestScore = -Float.MAX_VALUE;
+ HexPly bestMove = null;
+
+ 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);
+ float score = minimax(hb, MAXDEPTH, -Float.MAX_VALUE, Float.MAX_VALUE, true);
+ if (score > bestScore) {
+ bestScore = score;
+ bestMove = move;
+ }
+ hb.undoPly();
+ }
+ }
+ }
+ return bestMove;
+ }
+
+ private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing) {
+ if (depth == 0 || board.isGameOver()) {
+ 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);
+ float score = minimax(board, depth - 1, alpha, beta, false);
+ bestScore = Math.max(bestScore, score);
+ alpha = Math.max(alpha, bestScore);
+ if (beta <= alpha) break; // Pruning
+ 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);
+ float score = minimax(board, depth - 1, alpha, beta, true);
+ bestScore = Math.min(bestScore, score);
+ beta = Math.min(beta, bestScore);
+ if (beta <= alpha) break; // Pruning
+ board.undoPly();
+ }
+ }
+ }
+ return bestScore;
+ }
+ }
+
+ 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++) {
+ if (board.getPlayerAt(i, j) == Player.PLAYER1) {
+ score += Math.abs(i - center) + Math.abs(j - center); // Distance from center
+ }
+ }
+ }
+ return score;
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java b/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java
new file mode 100644
index 0000000..55a4e43
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java
@@ -0,0 +1,59 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+import java.util.Random;
+
+public class MonteCarloBot extends AbstractGamePlayer {
+
+ private static final int SIMULATION_COUNT = 1000;
+
+ public MonteCarloBot(Player me) {
+ super(me); // Correct constructor usage
+ }
+
+ @Override
+ public AbstractPly giveYourMove(IBoard board) {
+ HexBoard hb = (HexBoard) board;
+ float bestScore = -Float.MAX_VALUE;
+ HexPly bestMove = null;
+
+ 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);
+ float score = monteCarloSimulation(hb);
+ if (score > bestScore) {
+ bestScore = score;
+ bestMove = move;
+ }
+ hb.undoPly();
+ }
+ }
+ }
+ return bestMove;
+ }
+
+ private float monteCarloSimulation(HexBoard board) {
+ RandomBot simBot = new RandomBot(Player.PLAYER1, new Random().nextLong());
+ HexBoard simBoard = board.safeCopy();
+ int wins = 0;
+ int simulations = 0;
+
+ for (int i = 0; i < SIMULATION_COUNT; i++) {
+ while (!simBoard.isGameOver()) {
+ AbstractPly move = simBot.giveYourMove(simBoard);
+ simBoard.doPly(move);
+ }
+
+ if (simBoard.getResult() == Result.WIN) {
+ wins++;
+ }
+ simulations++;
+ simBoard = board.safeCopy(); // Reset the board for the next simulation
+ }
+
+ return (float) wins / simulations;
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java b/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
index 0bc5843..b670ada 100644
--- a/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
+++ b/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
@@ -10,9 +10,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Random;
-/**
- * Bot non intelligent : joue un coup légal au hasard.
- */
public class RandomBot extends AbstractGamePlayer {
private final Random rng;
@@ -28,7 +25,6 @@ public class RandomBot extends AbstractGamePlayer {
@Override
public AbstractPly giveYourMove(IBoard board) {
- // On récupère tous les coups légaux via l'itérateur fourni par le plateau.
List legal = new ArrayList<>();
Iterator it = board.iterator();
while (it.hasNext()) {
diff --git a/javaAPI/fr/iut_fbleau/HexGame/Simulation.java b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java
new file mode 100644
index 0000000..c18f87b
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java
@@ -0,0 +1,273 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+import java.util.EnumMap;
+import java.util.LinkedList;
+
+
+public class Simulation extends AbstractGame {
+
+ //ATTRIBUTS
+ private HexPly bestmove;
+ private float bestoutcome;
+ private int MAXDEPTH = 9;
+ private int EVALDEPTH = 10;
+ private LinkedList taken = new LinkedList();
+
+ //ATTRIBUTS QUE JE NE VOUDRAIS PAS CRÉER IDÉALEMENT
+ private IBoard simCurrentBoard;
+ private EnumMap simmapPlayers;
+
+ //CONSTRUCTEUR
+ public Simulation(IBoard b, EnumMap m){
+ super(b, m);
+ simCurrentBoard = b;
+ simmapPlayers = m;
+ }
+
+ //METHODES
+ /*Le jeu de Hex ne peut jamais finir avec le résultat null. En utilisant cette propriété, on peut avoir cet algorithme simplifié du monte-carlo*/
+ private float MonteCarlo(HexBoard position){
+ RandomBot simplay = new RandomBot();
+ HexBoard simpos = position.safeCopy();
+ LinkedList ctaken = taken;
+ HexPly testmove;
+ float wins = 0;
+ float losses = 0;
+
+ for(int i=0; i=losses){
+ return losses/wins;
+ } else {
+ return -(wins/losses);
+ }
+
+ }
+
+ private float explMAX(HexBoard position, int depth){
+ if (position.getResult()==Result.LOSS) {
+ return -1.0f;
+ } else if (position.getResult()==Result.WIN){
+ return 1.0f;
+ } else if (depth==MAXDEPTH) {
+ return MonteCarlo(position);
+ } else {
+ float bestcase = -1.0f;
+ HexPly bestcasemove;
+ HexPly testmove;
+ for (int i=0; i= bestcase) {
+ //System.out.println(" MAX new best case");
+ bestcase = val;
+ bestcasemove = testmove;
+ if (depth==0) {
+ this.bestoutcome = bestcase;
+ this.bestmove = bestcasemove;
+ }
+ }
+ position.undoPly();
+ taken.remove(t);
+ }
+ }
+ }
+ return bestcase;
+ }
+ }
+
+
+ private float explMIN(HexBoard position, int depth){
+ if (position.getResult()==Result.LOSS) {
+ return -1.0f;
+ } else if (position.getResult()==Result.WIN){
+ return 1.0f;
+ } else if (depth==MAXDEPTH) {
+ return MonteCarlo(position);
+ } else {
+ float bestcase = 1.0f;
+ HexPly bestcasemove;
+ HexPly testmove;
+ for (int i=0; i= bestcase) {
+ //System.out.println(" MAX new best case");
+ bestcase = val;
+ bestcasemove = testmove;
+ if (depth == 0) {
+ this.bestoutcome = bestcase;
+ this.bestmove = bestcasemove;
+ }
+ if (bestcase >= B) {
+ return bestcase;
+ }
+ }
+ position.undoPly();
+ taken.remove(t);
+ }
+ }
+ }
+ return bestcase;
+ }
+ }
+
+
+private float explMINAB(HexBoard position, int depth, float A, float B){
+ if (position.getResult() == Result.LOSS) {
+ return -1.0f;
+ } else if (position.getResult() == Result.WIN) {
+ return 1.0f;
+ } else if (depth == MAXDEPTH) {
+ return MonteCarlo(position);
+ } else {
+ float bestcase = B;
+ HexPly bestcasemove;
+ HexPly testmove;
+ for (int i = 0; i < position.getSize(); i++) {
+ for (int j = 0; j < position.getSize(); j++) {
+ if (depth == 0) {
+ //System.out.println("MIN New Line :");
+ }
+ Integer[] t = new Integer[]{i, j};
+ testmove = new HexPly(Player.PLAYER2, i, j);
+ if (!taken.contains(t) && position.isLegal(testmove)) {
+ //System.out.println(" MIN test move : "+Integer.toString(i)+","+Integer.toString(j));
+ taken.add(t);
+ position.doPly(testmove);
+ float val = explMAXAB(position, depth + 1, A, bestcase);
+ if (val <= bestcase) {
+ //System.out.println(" MIN new best case");
+ bestcase = val;
+ bestcasemove = testmove;
+ if (depth == 0) {
+ this.bestoutcome = bestcase;
+ this.bestmove = bestcasemove;
+ }
+ if (bestcase <= A) {
+ return bestcase;
+ }
+ }
+
+ position.undoPly();
+ taken.remove(t);
+ }
+ }
+ }
+ return bestcase;
+ }
+}
+
+
+
+
+ private AbstractPly GiveBestMove(IBoard board) {
+ if (!(board instanceof HexBoard)) {
+ throw new IllegalArgumentException("Ce joueur attend un HexBoard.");
+ }
+ HexBoard hb = (HexBoard) board;
+ float bestcase;
+ if(hb.getCurrentPlayer()==Player.PLAYER1){
+ bestcase = explMAXAB(hb, 0, -1.0f, 1.0f);
+ } else {
+ bestcase = explMINAB(hb, 0, -1.0f, 1.0f);
+ }
+ return this.bestmove;
+ }
+
+ @Override
+ public Result run(){
+ while(!simCurrentBoard.isGameOver()) {
+ AbstractGamePlayer player = simmapPlayers.get(simCurrentBoard.getCurrentPlayer());
+ IBoard board = simCurrentBoard.safeCopy();
+ AbstractPly ply = GiveBestMove(board);
+ HexPly concretePly = (HexPly) ply;
+
+ if (simCurrentBoard.isLegal(ply)) {
+ simCurrentBoard.doPly(ply);
+ taken.add(new Integer[]{concretePly.getRow(), concretePly.getCol()});
+ System.out.println("Player "+player+" goes ("+concretePly.getRow()+","+concretePly.getCol()+")");
+ }
+ else throw new IllegalStateException("Player "+ player + " is a bloody cheat. He tried playing : "+concretePly.getRow()+","+concretePly.getCol()+" I give up.");
+ }
+ return simCurrentBoard.getResult();
+ }
+
+
+}