Compare commits
1 Commits
MONTE_CARL
...
Bots-aleat
| Author | SHA1 | Date | |
|---|---|---|---|
| fa96aae6e6 |
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexFrame.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexFrame.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexPanel$1.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPanel$1.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexPanel.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPanel.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Args.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Args.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexSimMain.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/RandomBot.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/RandomBot.class
Normal file
Binary file not shown.
BIN
javaAPI/fr/iut_fbleau.tar
Normal file
BIN
javaAPI/fr/iut_fbleau.tar
Normal file
Binary file not shown.
@@ -34,8 +34,8 @@ public abstract class AbstractGame {
|
||||
|
||||
// constructeur à appeler dans le constructeur d'un fils concret avec super.
|
||||
public AbstractGame(IBoard b, EnumMap<Player,AbstractGamePlayer> m){
|
||||
this.currentBoard=b;
|
||||
this.mapPlayers=m;
|
||||
this.currentBoard=b;
|
||||
this.mapPlayers=m;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
52
javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
Normal file
52
javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
Normal file
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package fr.iut_fbleau.HexGame;
|
||||
|
||||
import fr.iut_fbleau.GameAPI.*;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Scanner;
|
||||
|
||||
@@ -18,19 +19,12 @@ public class HexMain {
|
||||
HexBoard board = new HexBoard(size);
|
||||
|
||||
Scanner sc = new Scanner(System.in);
|
||||
Result res;
|
||||
EnumMap<Player, AbstractGamePlayer> players = new EnumMap<>(Player.class);
|
||||
players.put(Player.PLAYER1, new HumanConsolePlayer(Player.PLAYER1, sc));
|
||||
players.put(Player.PLAYER2, new HumanConsolePlayer(Player.PLAYER2, sc));
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
AbstractGame game = new AbstractGame(board, players) {};
|
||||
Result res = game.run();
|
||||
|
||||
System.out.println(board);
|
||||
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
|
||||
|
||||
166
javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
Normal file
166
javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
204
javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
Normal file
204
javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
Normal file
@@ -0,0 +1,204 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
44
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
44
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
@@ -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;
|
||||
|
||||
/**
|
||||
* 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()));
|
||||
}
|
||||
}
|
||||
@@ -1,271 +0,0 @@
|
||||
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<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
|
||||
/*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<Integer[]> ctaken = taken;
|
||||
HexPly testmove;
|
||||
float wins = 0;
|
||||
float losses = 0;
|
||||
|
||||
for(int i=0; i<EVALDEPTH; i++){
|
||||
while(!simpos.isGameOver()){
|
||||
testmove = (HexPly) simplay.giveYourMove(simpos);
|
||||
if(!ctaken.contains(t) && simpos.isLegal(testmove)){
|
||||
ctaken.add(new Integer[]{testmove.getRow(), testmove.getCol()});
|
||||
simpos.doPly(testmove);
|
||||
if(simpos.getResult()==Result.LOSS){
|
||||
losses++;
|
||||
} else if(simpos.getResult()==Result.WIN){
|
||||
wins++;
|
||||
}
|
||||
}
|
||||
}
|
||||
simpos = position.safeCopy();
|
||||
ctaken = taken;
|
||||
}
|
||||
|
||||
if(wins>=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<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 MonteCarlo(position);
|
||||
} 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 float explMAXAB(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 = A;
|
||||
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 = explMINAB(position, depth+1, bestcase, B);
|
||||
if (val >= 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, A, 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
10001
results.csv
Normal file
10001
results.csv
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user