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/build/fr/iut_fbleau/HexGame/HeuristicBot.class b/build/fr/iut_fbleau/HexGame/HeuristicBot.class
new file mode 100644
index 0000000..0531221
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HeuristicBot.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexBoard.class b/build/fr/iut_fbleau/HexGame/HexBoard.class
index 8c58a16..654424d 100644
Binary files a/build/fr/iut_fbleau/HexGame/HexBoard.class and b/build/fr/iut_fbleau/HexGame/HexBoard.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexFrame.class b/build/fr/iut_fbleau/HexGame/HexFrame.class
new file mode 100644
index 0000000..29f983f
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexFrame.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexMain$1.class b/build/fr/iut_fbleau/HexGame/HexMain$1.class
index f572f6f..2cc6a34 100644
Binary files a/build/fr/iut_fbleau/HexGame/HexMain$1.class and b/build/fr/iut_fbleau/HexGame/HexMain$1.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexMain.class b/build/fr/iut_fbleau/HexGame/HexMain.class
index 10e072e..4782948 100644
Binary files a/build/fr/iut_fbleau/HexGame/HexMain.class and b/build/fr/iut_fbleau/HexGame/HexMain.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexPanel$1.class b/build/fr/iut_fbleau/HexGame/HexPanel$1.class
new file mode 100644
index 0000000..3166fc2
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexPanel$1.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexPanel.class b/build/fr/iut_fbleau/HexGame/HexPanel.class
new file mode 100644
index 0000000..78b3803
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexPanel.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexSimMain$Args.class b/build/fr/iut_fbleau/HexGame/HexSimMain$Args.class
new file mode 100644
index 0000000..3ac94f9
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexSimMain$Args.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class b/build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class
new file mode 100644
index 0000000..3f1560f
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class differ
diff --git a/build/fr/iut_fbleau/HexGame/HexSimMain.class b/build/fr/iut_fbleau/HexGame/HexSimMain.class
new file mode 100644
index 0000000..19a1a5b
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/HexSimMain.class differ
diff --git a/build/fr/iut_fbleau/HexGame/MiniMaxBot.class b/build/fr/iut_fbleau/HexGame/MiniMaxBot.class
new file mode 100644
index 0000000..e57fbb0
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/MiniMaxBot.class differ
diff --git a/build/fr/iut_fbleau/HexGame/MonteCarloBot.class b/build/fr/iut_fbleau/HexGame/MonteCarloBot.class
new file mode 100644
index 0000000..d745abd
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/MonteCarloBot.class differ
diff --git a/build/fr/iut_fbleau/HexGame/RandomBot.class b/build/fr/iut_fbleau/HexGame/RandomBot.class
new file mode 100644
index 0000000..ea379a6
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/RandomBot.class differ
diff --git a/build/fr/iut_fbleau/HexGame/Simulation.class b/build/fr/iut_fbleau/HexGame/Simulation.class
new file mode 100644
index 0000000..3c97095
Binary files /dev/null and b/build/fr/iut_fbleau/HexGame/Simulation.class differ
diff --git a/javaAPI/.~lock.arena_results.csv# b/javaAPI/.~lock.arena_results.csv#
new file mode 100644
index 0000000..e43af1b
--- /dev/null
+++ b/javaAPI/.~lock.arena_results.csv#
@@ -0,0 +1 @@
+,vaisse,salle235-12,06.02.2026 11:29,file:///export/home/an23/vaisse/.config/libreoffice/4;
\ No newline at end of file
diff --git a/javaAPI/arena_results.csv b/javaAPI/arena_results.csv
new file mode 100644
index 0000000..6b82172
--- /dev/null
+++ b/javaAPI/arena_results.csv
@@ -0,0 +1,7 @@
+Bot 1, Bot 2, Winner
+RandomBot,MiniMaxBot,WIN
+RandomBot,HeuristicBot,WIN
+RandomBot,MonteCarloBot,WIN
+MiniMaxBot,HeuristicBot,WIN
+MiniMaxBot,MonteCarloBot,WIN
+HeuristicBot,MonteCarloBot,WIN
diff --git a/javaAPI/fr/iut_fbleau/GameAPI/AbstractGamePlayer.java b/javaAPI/fr/iut_fbleau/GameAPI/AbstractGamePlayer.java
index c459eab..9b9e244 100644
--- a/javaAPI/fr/iut_fbleau/GameAPI/AbstractGamePlayer.java
+++ b/javaAPI/fr/iut_fbleau/GameAPI/AbstractGamePlayer.java
@@ -26,5 +26,6 @@ public abstract class AbstractGamePlayer {
* @throws IllegalStateException if the Situation is already in the bookmarks
*/
public abstract AbstractPly giveYourMove(IBoard p);
+ public abstract Boolean jesuisMinimax();
}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/Arena.java b/javaAPI/fr/iut_fbleau/HexGame/Arena.java
new file mode 100644
index 0000000..c2e2182
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/Arena.java
@@ -0,0 +1,64 @@
+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;
+ private int board_size;
+
+ public Arena(int size) {
+ try {
+ csvWriter = new FileWriter("arena_results.csv");
+ csvWriter.append("Bot 1, Bot 2, Winner\n");
+ this.board_size = size;
+ } 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(this.board_size);
+ EnumMap players = new EnumMap<>(Player.class);
+ players.put(Player.PLAYER1, bot1);
+ players.put(Player.PLAYER2, bot2);
+
+ Simulation simulation = new Simulation(board, players);
+ return simulation.runForArena();
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java
new file mode 100644
index 0000000..e0b9b8a
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/ArenaMain.java
@@ -0,0 +1,19 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.Player;
+
+public class ArenaMain {
+ public static void main(String[] args) {
+ int size = 7;
+ if (args.length >= 1) {
+ try { size = Integer.parseInt(args[0]); } catch (NumberFormatException ignored) {}
+ }
+ Arena arena = new Arena(size);
+ arena.addBot(new RandomBot(Player.PLAYER1, 24015L)); // 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..169a335
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/HeuristicBot.java
@@ -0,0 +1,53 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+public class HeuristicBot extends AbstractGamePlayer {
+
+ public HeuristicBot(Player me) {
+ super(me); // Correct constructor usage
+ }
+
+ public Boolean jesuisMinimax(){
+ return false;
+ }
+
+ @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;
+ //HexBoard simBoard = (HexBoard) board.safeCopy();
+
+ for (int i = 0; i < size; i++) {
+ for (int j = 0; j < size; j++) {
+ if (board.getCellPlayer(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/HexBoard.java b/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
index 3d5c7ca..c8f2646 100644
--- a/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
@@ -308,4 +308,10 @@ public class HexBoard extends AbstractBoard {
sb.append("Current player: ").append(getCurrentPlayer()).append("\n");
return sb.toString();
}
+
+
+ public Player getCellPlayer(int r, int c) {
+ return cells[r][c];
+ }
+
}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexFrame.java b/javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
new file mode 100644
index 0000000..992e8e5
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
@@ -0,0 +1,52 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.Player;
+import fr.iut_fbleau.GameAPI.Result;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class HexFrame {
+
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(() -> {
+ int size = 11;
+ if (args.length >= 1) {
+ try { size = Integer.parseInt(args[0]); } catch (NumberFormatException ignored) {}
+ }
+
+ HexBoard board = new HexBoard(size);
+
+ JFrame frame = new JFrame("Hex - " + size + "x" + size);
+ frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+ frame.setLayout(new BorderLayout());
+
+ JLabel statusLabel = new JLabel("", SwingConstants.CENTER);
+ statusLabel.setFont(statusLabel.getFont().deriveFont(Font.BOLD, 18f));
+ statusLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
+
+ HexPanel panel = new HexPanel(board, statusLabel);
+
+ frame.add(statusLabel, BorderLayout.NORTH);
+ frame.add(panel, BorderLayout.CENTER);
+
+ // Taille confortable
+ frame.pack();
+ frame.setLocationRelativeTo(null);
+ frame.setVisible(true);
+
+ // Message initial
+ updateStatus(board, statusLabel);
+ });
+ }
+
+ static void updateStatus(HexBoard board, JLabel statusLabel) {
+ if (board.isGameOver()) {
+ Result r = board.getResult(); // résultat du point de vue PLAYER1
+ Player winner = (r == Result.WIN) ? Player.PLAYER1 : Player.PLAYER2;
+ statusLabel.setText("" + winner + " a gagné !");
+ } else {
+ statusLabel.setText("C'est à " + board.getCurrentPlayer() + " de jouer");
+ }
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexMain.java b/javaAPI/fr/iut_fbleau/HexGame/HexMain.java
index e9d3885..df307da 100644
--- a/javaAPI/fr/iut_fbleau/HexGame/HexMain.java
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexMain.java
@@ -1,7 +1,6 @@
package fr.iut_fbleau.HexGame;
import fr.iut_fbleau.GameAPI.*;
-
import java.util.EnumMap;
import java.util.Scanner;
@@ -19,12 +18,19 @@ public class HexMain {
HexBoard board = new HexBoard(size);
Scanner sc = new Scanner(System.in);
+ Result res;
EnumMap players = new EnumMap<>(Player.class);
players.put(Player.PLAYER1, new HumanConsolePlayer(Player.PLAYER1, sc));
players.put(Player.PLAYER2, new HumanConsolePlayer(Player.PLAYER2, sc));
- AbstractGame game = new AbstractGame(board, players) {};
- Result res = game.run();
+
+ if (args.length>=2 && args[1].equals("autoplay")) {
+ Simulation sim = new Simulation(board, players);
+ res = sim.run();
+ } else {
+ AbstractGame game = new AbstractGame(board, players) {};
+ res = game.run();
+ }
System.out.println(board);
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexPanel.java b/javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
new file mode 100644
index 0000000..ab60f2d
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
@@ -0,0 +1,166 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.Player;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.awt.geom.Path2D;
+
+/**
+ * Panel Swing qui dessine un plateau Hex en hexagones et gère les clics.
+ *
+ * Grille "flat-top" (hexagones à sommet plat en haut),
+ * avec décalage vertical d'une demi-hauteur une colonne sur deux.
+ */
+public class HexPanel extends JPanel {
+
+ private final HexBoard board;
+ private final JLabel statusLabel;
+
+ // Rayon (distance centre -> sommet)
+ private final int s = 26;
+ private final int margin = 40;
+ // pointy-top : largeur = sqrt(3)*s, hauteur = 2*s
+ private final double hexW = Math.sqrt(3) * s;
+ private final double hexVStep = 1.5 * s; // distance verticale entre centres
+
+
+ private Shape[][] hexShapes;
+
+ public HexPanel(HexBoard board, JLabel statusLabel) {
+ this.board = board;
+ this.statusLabel = statusLabel;
+ this.hexShapes = new Shape[board.getSize()][board.getSize()];
+
+ setBackground(Color.WHITE);
+
+ addMouseListener(new MouseAdapter() {
+ @Override
+ public void mouseClicked(MouseEvent e) {
+ handleClick(e.getX(), e.getY());
+ }
+ });
+ }
+
+ @Override
+ public Dimension getPreferredSize() {
+ int n = board.getSize();
+
+ // largeur : n * hexW + décalage max (hexW/2) + marges
+ int w = margin * 2 + (int) (n * hexW + hexW / 2);
+
+ // hauteur : (n-1)*1.5*s + 2*s + marges
+ int h = margin * 2 + (int) ((n - 1) * hexVStep + 2 * s);
+
+ return new Dimension(w, h);
+ }
+
+
+ private void handleClick(int x, int y) {
+ if (board.isGameOver()) return;
+
+ int n = board.getSize();
+ for (int row = 0; row < n; row++) {
+ for (int col = 0; col < n; col++) {
+ Shape sh = hexShapes[row][col];
+ if (sh != null && sh.contains(x, y)) {
+ HexPly ply = new HexPly(board.getCurrentPlayer(), row, col);
+ if (board.isLegal(ply)) {
+ board.doPly(ply);
+ HexFrame.updateStatus(board, statusLabel);
+ repaint();
+ } else {
+ Toolkit.getDefaultToolkit().beep();
+ }
+ return;
+ }
+ }
+ }
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ super.paintComponent(g);
+
+ Graphics2D g2 = (Graphics2D) g.create();
+ g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+
+ // Bordures objectifs (bleu gauche/droite, rouge haut/bas)
+ drawGoalBorders(g2);
+
+ int n = board.getSize();
+
+ // IMPORTANT : boucles cohérentes -> row puis col
+ for (int row = 0; row < n; row++) {
+ for (int col = 0; col < n; col++) {
+
+ Shape hex = createHexShape(row, col);
+ hexShapes[row][col] = hex;
+
+ Player p = board.getCellPlayer(row, col);
+ g2.setColor(colorForCell(p));
+ g2.fill(hex);
+
+ g2.setColor(new Color(120, 120, 120));
+ g2.setStroke(new BasicStroke(1.2f));
+ g2.draw(hex);
+ }
+ }
+
+ g2.dispose();
+ }
+
+ private Color colorForCell(Player p) {
+ if (p == Player.PLAYER1) return new Color(30, 90, 160); // bleu
+ if (p == Player.PLAYER2) return new Color(220, 50, 50); // rouge
+ return new Color(190, 190, 190); // gris
+ }
+
+ /**
+ * Pointy-top + décalage par ligne :
+ *
+ * centreX = margin + hexW/2 + col*hexW + (row%2)*(hexW/2)
+ * centreY = margin + s + row*(1.5*s)
+ */
+ private Shape createHexShape(int row, int col) {
+ double cx = margin + (hexW / 2.0) + col * hexW + ((row % 2) * (hexW / 2.0));
+ double cy = margin + s + row * hexVStep;
+
+ Path2D.Double path = new Path2D.Double();
+ for (int i = 0; i < 6; i++) {
+ double angle = Math.toRadians(i * 60); // pointy-top
+ double x = cx + s * Math.cos(angle);
+ double y = cy + s * Math.sin(angle);
+ if (i == 0) path.moveTo(x, y);
+ else path.lineTo(x, y);
+ }
+ path.closePath();
+ return path;
+ }
+
+
+ private void drawGoalBorders(Graphics2D g2) {
+ int n = board.getSize();
+
+ double leftX = margin - 12;
+ double rightX = margin + (hexW / 2.0) + (n - 1) * hexW + (hexW / 2.0) + (hexW / 2.0) + 12;
+ // explication: largeur n colonnes + potentiel décalage d'une demi-largeur
+
+ double topY = margin - 12;
+ double bottomY = margin + s + (n - 1) * hexVStep + s + 12;
+
+ g2.setStroke(new BasicStroke(6f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
+
+ // Bleu: gauche / droite (objectif PLAYER1)
+ g2.setColor(new Color(30, 90, 160));
+ g2.drawLine((int) leftX, (int) topY, (int) leftX, (int) bottomY);
+ g2.drawLine((int) rightX, (int) topY, (int) rightX, (int) bottomY);
+
+ // Rouge: haut / bas (objectif PLAYER2)
+ g2.setColor(new Color(220, 50, 50));
+ g2.drawLine((int) leftX, (int) topY, (int) rightX, (int) topY);
+ g2.drawLine((int) leftX, (int) bottomY, (int) rightX, (int) bottomY);
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
new file mode 100644
index 0000000..d314aa7
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
@@ -0,0 +1,217 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.EnumMap;
+import java.util.Random;
+
+/**
+ * Lance un grand nombre de parties Hex entre 2 bots aléatoires et affiche des stats.
+ *
+ * Exemples :
+ * 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 {
+
+ private static class Stats {
+ long win = 0;
+ long draw = 0;
+ long loss = 0;
+
+ long totalMoves = 0;
+ long minMoves = Long.MAX_VALUE;
+ long maxMoves = Long.MIN_VALUE;
+
+ void record(Result r, int moves) {
+ if (r == Result.WIN) win++;
+ else if (r == Result.DRAW) draw++;
+ else if (r == Result.LOSS) loss++;
+ totalMoves += moves;
+ minMoves = Math.min(minMoves, moves);
+ maxMoves = Math.max(maxMoves, moves);
+ }
+
+ long games() { return win + draw + loss; }
+
+ double winRate() { return games() == 0 ? 0.0 : (double) win / games(); }
+ double drawRate() { return games() == 0 ? 0.0 : (double) draw / games(); }
+ double lossRate() { return games() == 0 ? 0.0 : (double) loss / games(); }
+ double avgMoves() { return games() == 0 ? 0.0 : (double) totalMoves / games(); }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Games: ").append(games()).append("\n");
+ sb.append("WIN: ").append(win).append(String.format(" (%.2f%%)\n", 100.0 * winRate()));
+ sb.append("DRAW: ").append(draw).append(String.format(" (%.2f%%)\n", 100.0 * drawRate()));
+ sb.append("LOSS: ").append(loss).append(String.format(" (%.2f%%)\n", 100.0 * lossRate()));
+ sb.append(String.format("Moves: avg=%.2f, min=%d, max=%d\n", avgMoves(), minMoves, maxMoves));
+ return sb.toString();
+ }
+ }
+
+ private static class Args {
+ int size = 7;
+ int games = 1000;
+ long seed = System.currentTimeMillis();
+ int progressEvery = 0; // 0 = pas de progress
+ String csvPath = null; // si non null, export par partie
+ }
+
+ public static void main(String[] args) {
+ Args a = parseArgs(args);
+
+ System.out.println("Hex random-vs-random simulation");
+ System.out.println(" size=" + a.size + " games=" + a.games + " seed=" + a.seed +
+ (a.csvPath != null ? " csv=" + a.csvPath : ""));
+
+ Random master = new Random(a.seed);
+ Stats stats = new Stats();
+
+ BufferedWriter csv = null;
+ try {
+ if (a.csvPath != null) {
+ csv = new BufferedWriter(new FileWriter(a.csvPath));
+ csv.write("game_index,result_p1,moves\n");
+ }
+
+ for (int i = 0; i < a.games; i++) {
+ // Nouveau plateau, nouveaux bots (seeds dérivés du seed principal)
+ HexBoard board = new HexBoard(a.size);
+
+ EnumMap players = new EnumMap<>(Player.class);
+ players.put(Player.PLAYER1, new RandomBot(Player.PLAYER1, new Random(master.nextLong())));
+ players.put(Player.PLAYER2, new RandomBot(Player.PLAYER2, new Random(master.nextLong())));
+
+ int moves = runOneGame(board, players);
+
+ Result res = board.getResult();
+ stats.record(res, moves);
+
+ if (csv != null) {
+ csv.write(i + "," + res + "," + moves + "\n");
+ }
+
+ if (a.progressEvery > 0 && (i + 1) % a.progressEvery == 0) {
+ System.out.println("Progress: " + (i + 1) + "/" + a.games);
+ }
+ }
+
+ System.out.println("\n=== SUMMARY (Result is from PLAYER1 perspective) ===");
+ System.out.println(stats);
+
+ } catch (IOException e) {
+ System.err.println("I/O error: " + e.getMessage());
+ e.printStackTrace();
+ } finally {
+ if (csv != null) {
+ try { csv.close(); } catch (IOException ignored) {}
+ }
+ }
+ }
+
+ /**
+ * Boucle de jeu (même logique que AbstractGame.run, mais on compte les coups).
+ * On ne modifie pas GameAPI.
+ */
+ private static int runOneGame(IBoard board, EnumMap players) {
+ int moves = 0;
+ int guardMaxMoves = ((HexBoard) board).getSize() * ((HexBoard) board).getSize(); // au pire : plateau rempli
+
+ while (!board.isGameOver()) {
+ AbstractGamePlayer p = players.get(board.getCurrentPlayer());
+ IBoard safe = board.safeCopy();
+ AbstractPly ply = p.giveYourMove(safe);
+
+ if (!board.isLegal(ply)) {
+ throw new IllegalStateException("Illegal move: " + ply + " by " + board.getCurrentPlayer());
+ }
+ board.doPly(ply);
+ moves++;
+
+ if (moves > guardMaxMoves) {
+ throw new IllegalStateException("Too many moves (" + moves + "), something is wrong.");
+ }
+ }
+ return moves;
+ }
+
+ private static Args parseArgs(String[] args) {
+ Args a = new Args();
+ for (int i = 0; i < args.length; i++) {
+ String s = args[i];
+ switch (s) {
+ case "--size":
+ a.size = Integer.parseInt(nextArg(args, ++i, "--size requires a value"));
+ break;
+ case "--games":
+ a.games = Integer.parseInt(nextArg(args, ++i, "--games requires a value"));
+ break;
+ case "--seed":
+ a.seed = Long.parseLong(nextArg(args, ++i, "--seed requires a value"));
+ break;
+ case "--progress":
+ a.progressEvery = Integer.parseInt(nextArg(args, ++i, "--progress requires a value"));
+ break;
+ case "--csv":
+ a.csvPath = nextArg(args, ++i, "--csv requires a value");
+ break;
+ case "--help":
+ case "-h":
+ printHelpAndExit();
+ break;
+ default:
+ // compat: si l'utilisateur donne juste un nombre, on l'interprète comme size ou games
+ // ex: "7 10000"
+ if (isInt(s)) {
+ int v = Integer.parseInt(s);
+ if (a.size == 11) a.size = v;
+ else a.games = v;
+ } else {
+ System.err.println("Unknown arg: " + s);
+ printHelpAndExit();
+ }
+ }
+ }
+ return a;
+ }
+
+ private static String nextArg(String[] args, int idx, String errMsg) {
+ if (idx < 0 || idx >= args.length) throw new IllegalArgumentException(errMsg);
+ return args[idx];
+ }
+
+ private static boolean isInt(String s) {
+ try { Integer.parseInt(s); return true; } catch (NumberFormatException e) { return false; }
+ }
+
+ private static void printHelpAndExit() {
+ System.out.println("Usage: java fr.iut_fbleau.HexGame.HexSimMain [options]\n" +
+ "Options:\n" +
+ " --size N Board size (default 7)\n" +
+ " --games N Number of games (default 1000)\n" +
+ " --seed N Random seed (default current time)\n" +
+ " --progress N Print progress every N games (default 0)\n" +
+ " --csv FILE Write per-game results to CSV\n" +
+ " -h, --help Show this help\n");
+ System.exit(0);
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java b/javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
index 63068fc..49886f2 100644
--- a/javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
+++ b/javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
@@ -18,6 +18,10 @@ public class HumanConsolePlayer extends AbstractGamePlayer {
this.in = in;
}
+ public Boolean jesuisMinimax(){
+ return false;
+ }
+
@Override
public AbstractPly giveYourMove(IBoard board) {
if (!(board instanceof HexBoard)) {
diff --git a/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java
new file mode 100644
index 0000000..dfa9d81
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/MiniMaxBot.java
@@ -0,0 +1,93 @@
+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
+ }
+
+ public Boolean jesuisMinimax(){
+ return true;
+ }
+
+ @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.getCellPlayer(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..333b655
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/MonteCarloBot.java
@@ -0,0 +1,63 @@
+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
+ }
+
+ public Boolean jesuisMinimax(){
+ return false;
+ }
+
+ @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 = (HexBoard) 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 = (HexBoard) 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
new file mode 100644
index 0000000..8d29af7
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
@@ -0,0 +1,44 @@
+package fr.iut_fbleau.HexGame;
+
+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 java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+public class RandomBot extends AbstractGamePlayer {
+
+ private final Random rng;
+
+ public RandomBot(Player me, Random rng) {
+ super(me);
+ this.rng = rng;
+ }
+
+ public Boolean jesuisMinimax(){
+ return false;
+ }
+
+ public RandomBot(Player me, long seed) {
+ this(me, new Random(seed));
+ }
+
+ @Override
+ public AbstractPly giveYourMove(IBoard board) {
+ List legal = new ArrayList<>();
+ Iterator it = board.iterator();
+ while (it.hasNext()) {
+ legal.add(it.next());
+ }
+
+ if (legal.isEmpty()) {
+ throw new IllegalStateException("No legal move available (board is full?)");
+ }
+
+ return legal.get(rng.nextInt(legal.size()));
+ }
+}
diff --git a/javaAPI/fr/iut_fbleau/HexGame/Simulation.java b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java
new file mode 100644
index 0000000..95ad2ac
--- /dev/null
+++ b/javaAPI/fr/iut_fbleau/HexGame/Simulation.java
@@ -0,0 +1,293 @@
+package fr.iut_fbleau.HexGame;
+
+import fr.iut_fbleau.GameAPI.*;
+import java.util.EnumMap;
+import java.util.LinkedList;
+import java.util.Random;
+
+
+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, Player current){
+ RandomBot simplay = new RandomBot(current, new Random().nextLong());
+ HexBoard simpos = position;
+ LinkedList ctaken = taken;
+ HexPly testmove;
+ float wins = 0;
+ float losses = 0;
+ int count = 0;
+ 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, Player.PLAYER2);
+ } 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, Player.PLAYER2);
+ } else {
+ float bestcase = B;
+ HexPly bestcasemove;
+ HexPly testmove;
+ for (int i=0; i