Compare commits
5 Commits
Bots-aleat
...
kara-mosr-
| Author | SHA1 | Date | |
|---|---|---|---|
| db6db8e6c2 | |||
| 2dfc6014e0 | |||
| 3aec1d3f6e | |||
| a7d3e9d138 | |||
| f207da0e2b |
@@ -105,6 +105,7 @@ Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
|||||||
git commit -m "Ajout de [...] #numeroticket"
|
git commit -m "Ajout de [...] #numeroticket"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 4. Pousser la branche
|
## 4. Pousser la branche
|
||||||
|
|
||||||
git push -set-upstream origin <nom-de-la-branche-#numeroticket>
|
git push -set-upstream origin <nom-de-la-branche-#numeroticket>
|
||||||
@@ -112,4 +113,9 @@ Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
|||||||
|
|
||||||
## 5. Supprimer une branche
|
## 5. Supprimer une branche
|
||||||
|
|
||||||
git branch -d <nom_de_la_branche>
|
git branch -d <nom_de_la_branche>
|
||||||
|
|
||||||
|
## 6. Lancer des parties avec bots aleatoires
|
||||||
|
|
||||||
|
javac -d build $(find . -name "*.java")
|
||||||
|
java -cp build fr.iut_fbleau.HexGame.HexSimMain --games 10000 --size 11 --seed 123 --csv results.csv
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -34,8 +34,8 @@ public abstract class AbstractGame {
|
|||||||
|
|
||||||
// constructeur à appeler dans le constructeur d'un fils concret avec super.
|
// constructeur à appeler dans le constructeur d'un fils concret avec super.
|
||||||
public AbstractGame(IBoard b, EnumMap<Player,AbstractGamePlayer> m){
|
public AbstractGame(IBoard b, EnumMap<Player,AbstractGamePlayer> m){
|
||||||
this.currentBoard=b;
|
this.currentBoard=b;
|
||||||
this.mapPlayers=m;
|
this.mapPlayers=m;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -308,10 +308,4 @@ public class HexBoard extends AbstractBoard {
|
|||||||
sb.append("Current player: ").append(getCurrentPlayer()).append("\n");
|
sb.append("Current player: ").append(getCurrentPlayer()).append("\n");
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public Player getCellPlayer(int r, int c) {
|
|
||||||
return cells[r][c];
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,52 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package fr.iut_fbleau.HexGame;
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
import fr.iut_fbleau.GameAPI.*;
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.Scanner;
|
import java.util.Scanner;
|
||||||
|
|
||||||
@@ -19,12 +18,19 @@ public class HexMain {
|
|||||||
HexBoard board = new HexBoard(size);
|
HexBoard board = new HexBoard(size);
|
||||||
|
|
||||||
Scanner sc = new Scanner(System.in);
|
Scanner sc = new Scanner(System.in);
|
||||||
|
Result res;
|
||||||
EnumMap<Player, AbstractGamePlayer> players = new EnumMap<>(Player.class);
|
EnumMap<Player, AbstractGamePlayer> players = new EnumMap<>(Player.class);
|
||||||
players.put(Player.PLAYER1, new HumanConsolePlayer(Player.PLAYER1, sc));
|
players.put(Player.PLAYER1, new HumanConsolePlayer(Player.PLAYER1, sc));
|
||||||
players.put(Player.PLAYER2, new HumanConsolePlayer(Player.PLAYER2, 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(board);
|
||||||
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
|
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
|
||||||
|
|||||||
@@ -1,166 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,204 +0,0 @@
|
|||||||
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
|
|
||||||
*/
|
|
||||||
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<Player, AbstractGamePlayer> 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<Player, AbstractGamePlayer> 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 == 7) 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
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;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bot non intelligent : joue un coup légal au hasard.
|
|
||||||
*/
|
|
||||||
public class RandomBot extends AbstractGamePlayer {
|
|
||||||
|
|
||||||
private final Random rng;
|
|
||||||
|
|
||||||
public RandomBot(Player me, Random rng) {
|
|
||||||
super(me);
|
|
||||||
this.rng = rng;
|
|
||||||
}
|
|
||||||
|
|
||||||
public RandomBot(Player me, long seed) {
|
|
||||||
this(me, new Random(seed));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AbstractPly giveYourMove(IBoard board) {
|
|
||||||
// On récupère tous les coups légaux via l'itérateur fourni par le plateau.
|
|
||||||
List<AbstractPly> legal = new ArrayList<>();
|
|
||||||
Iterator<AbstractPly> 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()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
145
javaAPI/fr/iut_fbleau/HexGame/Simulation.java
Normal file
145
javaAPI/fr/iut_fbleau/HexGame/Simulation.java
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
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 = 6;
|
||||||
|
private LinkedList<Integer[]> taken = new LinkedList<Integer[]>();
|
||||||
|
|
||||||
|
//ATTRIBUTS QUE JE NE VOUDRAIS PAS CRÉER IDÉALEMENT
|
||||||
|
private IBoard simCurrentBoard;
|
||||||
|
private EnumMap<Player, AbstractGamePlayer> simmapPlayers;
|
||||||
|
|
||||||
|
//CONSTRUCTEUR
|
||||||
|
public Simulation(IBoard b, EnumMap<Player,AbstractGamePlayer> m){
|
||||||
|
super(b, m);
|
||||||
|
simCurrentBoard = b;
|
||||||
|
simmapPlayers = m;
|
||||||
|
}
|
||||||
|
|
||||||
|
//METHODES
|
||||||
|
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 0f;
|
||||||
|
} else {
|
||||||
|
float bestcase = -1.0f;
|
||||||
|
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("MAX New Line :");
|
||||||
|
}
|
||||||
|
Integer[] t = new Integer[]{i, j};
|
||||||
|
testmove = new HexPly(Player.PLAYER1, i, j);
|
||||||
|
if(!taken.contains(t) && position.isLegal(testmove)){
|
||||||
|
//System.out.println(" MAX test move : "+Integer.toString(i)+","+Integer.toString(j));
|
||||||
|
taken.add(t);
|
||||||
|
position.doPly(testmove);
|
||||||
|
float val = explMIN(position, depth+1);
|
||||||
|
if (val >= 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 0f;
|
||||||
|
} else {
|
||||||
|
float bestcase = 1.0f;
|
||||||
|
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 = explMAX(position, depth+1);
|
||||||
|
if (val <= bestcase) {
|
||||||
|
//System.out.println(" MIN new best case");
|
||||||
|
bestcase = val;
|
||||||
|
bestcasemove = testmove;
|
||||||
|
if (depth==0) {
|
||||||
|
this.bestoutcome = bestcase;
|
||||||
|
this.bestmove = bestcasemove;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = explMAX(hb, 0);
|
||||||
|
} else {
|
||||||
|
bestcase = explMIN(hb, 0);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
10001
results.csv
10001
results.csv
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user