Compare commits
8 Commits
classe_tui
...
MONTE_CARL
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4b2b37a04a | ||
| 2dfc6014e0 | |||
| 3aec1d3f6e | |||
| a7d3e9d138 | |||
| f207da0e2b | |||
| 22891ae2b6 | |||
| d8ea5cd958 | |||
| d2a1cb0d65 |
25
HexPly.java
25
HexPly.java
@@ -1,25 +0,0 @@
|
||||
|
||||
public class HexPly extends AbstractPly{
|
||||
//attributs
|
||||
private byte y;
|
||||
private byte r;
|
||||
private String color;
|
||||
|
||||
//méthodes
|
||||
public Tuile attemptPlaceTuile(){
|
||||
Tuile t = new Tuile(this.y, this.r, this.color);
|
||||
return t;
|
||||
}
|
||||
|
||||
//constructeur
|
||||
public HexPly(byte y, byte r, Player p){
|
||||
this.y = y;
|
||||
this.r = r;
|
||||
this.joueur = p;
|
||||
if(p == Player.PLAYER1){
|
||||
this.color = "blue";
|
||||
} else {
|
||||
this.color = "red";
|
||||
}
|
||||
}
|
||||
}
|
||||
37
Tuile.java
37
Tuile.java
@@ -1,37 +0,0 @@
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class Tuile {
|
||||
/*ATTRIBUTS*/
|
||||
private byte y;
|
||||
private byte r;
|
||||
private String color;
|
||||
private LinkedList<Tuile> voisins = new LinkedList<Tuile>();
|
||||
|
||||
/*METHODES*/
|
||||
public byte getY(){
|
||||
return this.y;
|
||||
}
|
||||
|
||||
public byte getR(){
|
||||
return this.r;
|
||||
}
|
||||
|
||||
public String getColor(){
|
||||
return this.color;
|
||||
}
|
||||
|
||||
public void addVoisin(Tuile v){
|
||||
this.voisins.add(v);
|
||||
}
|
||||
|
||||
public LinkedList<Tuile> getVoisins(){
|
||||
return this.voisins;
|
||||
}
|
||||
|
||||
/*CONSTRUCTEUR*/
|
||||
public Tuile(byte y, byte r, String c){
|
||||
this.y = y;
|
||||
this.r = r;
|
||||
this.color = c;
|
||||
}
|
||||
}
|
||||
BIN
build/fr/iut_fbleau/GameAPI/AbstractBoard.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractGame.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractGame.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractGamePlayer.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractGamePlayer.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractPly.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractPly.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/IBoard.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/IBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/Player.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/Player.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/Result.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/Result.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexBoard.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexMain$1.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexMain$1.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexMain.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexMain.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexPly.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPly.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HumanConsolePlayer.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HumanConsolePlayer.class
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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
311
javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
Normal file
311
javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
Normal file
@@ -0,0 +1,311 @@
|
||||
package fr.iut_fbleau.HexGame;
|
||||
|
||||
import fr.iut_fbleau.GameAPI.*;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Représente le plateau du jeu de Hex.
|
||||
*
|
||||
* <h2>Rappel des conditions de victoire</h2>
|
||||
* <ul>
|
||||
* <li>{@link Player#PLAYER1} gagne s'il existe un chemin de pions connectés
|
||||
* reliant le bord gauche au bord droit.</li>
|
||||
* <li>{@link Player#PLAYER2} gagne s'il existe un chemin de pions connectés
|
||||
* reliant le bord haut au bord bas.</li>
|
||||
* </ul>
|
||||
*
|
||||
* <h2>Idée de l'algorithme de détection de victoire</h2>
|
||||
* On modélise le plateau comme un graphe :
|
||||
* <ul>
|
||||
* <li>Chaque case est un sommet</li>
|
||||
* <li>Deux cases sont connectées si elles sont voisines sur la grille hexagonale (6 voisins)</li>
|
||||
* </ul>
|
||||
*
|
||||
* Pour tester la victoire d'un joueur, on lance un parcours (DFS/BFS) :
|
||||
* <ol>
|
||||
* <li>On part de toutes les cases du bord de départ qui contiennent un pion du joueur.</li>
|
||||
* <li>On explore tous les pions du joueur connectés à ces cases.</li>
|
||||
* <li>Si on atteint le bord opposé, il existe un chemin : le joueur gagne.</li>
|
||||
* </ol>
|
||||
*
|
||||
* Complexité : O(N²) au pire (on visite chaque case au plus une fois).
|
||||
*/
|
||||
public class HexBoard extends AbstractBoard {
|
||||
|
||||
/** Taille du plateau : size x size. */
|
||||
private final int size;
|
||||
|
||||
/**
|
||||
* Grille des cases.
|
||||
* Une case vaut :
|
||||
* <ul>
|
||||
* <li>null : case vide</li>
|
||||
* <li>PLAYER1 : pion du joueur 1</li>
|
||||
* <li>PLAYER2 : pion du joueur 2</li>
|
||||
* </ul>
|
||||
*/
|
||||
private final Player[][] cells;
|
||||
|
||||
/**
|
||||
* Offsets des 6 voisins d'une case dans une grille hexagonale.
|
||||
*
|
||||
* Pour une case (r,c), les voisins potentiels sont :
|
||||
* (r-1,c), (r+1,c), (r,c-1), (r,c+1), (r-1,c+1), (r+1,c-1).
|
||||
*/
|
||||
private static final int[][] NEIGHBORS = {
|
||||
{-1, 0}, {+1, 0},
|
||||
{ 0, -1}, { 0, +1},
|
||||
{-1, +1}, {+1, -1}
|
||||
};
|
||||
|
||||
/** Crée un plateau vide avec {@link Player#PLAYER1} qui commence. */
|
||||
public HexBoard(int size) {
|
||||
this(size, Player.PLAYER1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur interne, utile pour {@link #safeCopy()}.
|
||||
* @param size taille du plateau
|
||||
* @param current joueur courant
|
||||
*/
|
||||
private HexBoard(int size, Player current) {
|
||||
super(current, new ArrayDeque<>());
|
||||
if (size <= 0) throw new IllegalArgumentException("size must be > 0");
|
||||
this.size = size;
|
||||
this.cells = new Player[size][size];
|
||||
}
|
||||
|
||||
/** @return la taille du plateau. */
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si (r,c) est dans le plateau.
|
||||
* @param r ligne (0..size-1)
|
||||
* @param c colonne (0..size-1)
|
||||
*/
|
||||
private boolean inBounds(int r, int c) {
|
||||
return r >= 0 && r < size && c >= 0 && c < size;
|
||||
}
|
||||
|
||||
/** @return le contenu d'une case (null si vide). */
|
||||
private Player getCell(int r, int c) {
|
||||
return cells[r][c];
|
||||
}
|
||||
|
||||
/** Modifie une case (utilisé par doPly/undoPly). */
|
||||
private void setCell(int r, int c, Player p) {
|
||||
cells[r][c] = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste la victoire de PLAYER1 (gauche -> droite).
|
||||
*
|
||||
* <h3>Détails de l'algorithme</h3>
|
||||
* <ol>
|
||||
* <li>On initialise une structure "visited" pour ne pas revisiter les cases.</li>
|
||||
* <li>On met dans une pile toutes les cases du bord gauche (colonne 0)
|
||||
* qui contiennent un pion PLAYER1.</li>
|
||||
* <li>On effectue un DFS :
|
||||
* <ul>
|
||||
* <li>on dépile une case</li>
|
||||
* <li>si elle est sur la colonne size-1 : on a touché le bord droit -> victoire</li>
|
||||
* <li>sinon, on empile tous ses voisins qui sont des pions PLAYER1 et pas encore visités</li>
|
||||
* </ul>
|
||||
* </li>
|
||||
* </ol>
|
||||
*
|
||||
* @return true si PLAYER1 a un chemin gauche->droite, false sinon
|
||||
*/
|
||||
private boolean hasPlayer1Won() {
|
||||
boolean[][] visited = new boolean[size][size];
|
||||
Deque<int[]> stack = new ArrayDeque<>();
|
||||
|
||||
// 1) points de départ : bord gauche
|
||||
for (int r = 0; r < size; r++) {
|
||||
if (getCell(r, 0) == Player.PLAYER1) {
|
||||
visited[r][0] = true;
|
||||
stack.push(new int[]{r, 0});
|
||||
}
|
||||
}
|
||||
|
||||
// 2) DFS
|
||||
while (!stack.isEmpty()) {
|
||||
int[] cur = stack.pop();
|
||||
int cr = cur[0], cc = cur[1];
|
||||
|
||||
// condition d'arrivée : bord droit
|
||||
if (cc == size - 1) return true;
|
||||
|
||||
// explore les 6 voisins
|
||||
for (int[] d : NEIGHBORS) {
|
||||
int nr = cr + d[0], nc = cc + d[1];
|
||||
if (inBounds(nr, nc)
|
||||
&& !visited[nr][nc]
|
||||
&& getCell(nr, nc) == Player.PLAYER1) {
|
||||
visited[nr][nc] = true;
|
||||
stack.push(new int[]{nr, nc});
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste la victoire de PLAYER2 (haut -> bas).
|
||||
*
|
||||
* Même principe que {@link #hasPlayer1Won()} mais :
|
||||
* <ul>
|
||||
* <li>Départ : bord haut (ligne 0)</li>
|
||||
* <li>Arrivée : bord bas (ligne size-1)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @return true si PLAYER2 a un chemin haut->bas, false sinon
|
||||
*/
|
||||
private boolean hasPlayer2Won() {
|
||||
boolean[][] visited = new boolean[size][size];
|
||||
Deque<int[]> stack = new ArrayDeque<>();
|
||||
|
||||
// points de départ : bord haut
|
||||
for (int c = 0; c < size; c++) {
|
||||
if (getCell(0, c) == Player.PLAYER2) {
|
||||
visited[0][c] = true;
|
||||
stack.push(new int[]{0, c});
|
||||
}
|
||||
}
|
||||
|
||||
// DFS
|
||||
while (!stack.isEmpty()) {
|
||||
int[] cur = stack.pop();
|
||||
int cr = cur[0], cc = cur[1];
|
||||
|
||||
// condition d'arrivée : bord bas
|
||||
if (cr == size - 1) return true;
|
||||
|
||||
for (int[] d : NEIGHBORS) {
|
||||
int nr = cr + d[0], nc = cc + d[1];
|
||||
if (inBounds(nr, nc)
|
||||
&& !visited[nr][nc]
|
||||
&& getCell(nr, nc) == Player.PLAYER2) {
|
||||
visited[nr][nc] = true;
|
||||
stack.push(new int[]{nr, nc});
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegal(AbstractPly move) {
|
||||
if (!(move instanceof HexPly)) return false;
|
||||
HexPly hp = (HexPly) move;
|
||||
int r = hp.getRow(), c = hp.getCol();
|
||||
return inBounds(r, c)
|
||||
&& getCell(r, c) == null
|
||||
&& hp.getPlayer() == getCurrentPlayer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Teste si un coup est immédiatement gagnant.
|
||||
*
|
||||
* On joue le coup, on teste la victoire, puis on annule le coup.
|
||||
* Cela permet d'évaluer un coup sans modifier définitivement l'état du plateau.
|
||||
*
|
||||
* @param move coup à tester
|
||||
* @return true si après ce coup le joueur a gagné, false sinon
|
||||
*/
|
||||
public boolean isWinningMove(AbstractPly move) {
|
||||
if (!isLegal(move)) return false;
|
||||
Player p = move.getPlayer();
|
||||
|
||||
doPly(move);
|
||||
boolean winNow = (p == Player.PLAYER1) ? hasPlayer1Won() : hasPlayer2Won();
|
||||
undoPly();
|
||||
|
||||
return winNow;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doPly(AbstractPly move) {
|
||||
if (!(move instanceof HexPly)) {
|
||||
throw new IllegalArgumentException("Coup invalide: " + move);
|
||||
}
|
||||
if (!isLegal(move)) {
|
||||
throw new IllegalStateException("Coup illégal: " + move);
|
||||
}
|
||||
|
||||
HexPly hp = (HexPly) move;
|
||||
setCell(hp.getRow(), hp.getCol(), hp.getPlayer());
|
||||
addPlyToHistory(move);
|
||||
setNextPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void undoPly() {
|
||||
AbstractPly last = removePlyFromHistory();
|
||||
HexPly hp = (HexPly) last;
|
||||
|
||||
setCell(hp.getRow(), hp.getCol(), null);
|
||||
setNextPlayer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGameOver() {
|
||||
return hasPlayer1Won() || hasPlayer2Won();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result getResult() {
|
||||
if (!isGameOver()) return null;
|
||||
if (hasPlayer1Won()) return Result.WIN; // du point de vue PLAYER1
|
||||
return Result.LOSS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<AbstractPly> iterator() {
|
||||
Player me = getCurrentPlayer();
|
||||
List<AbstractPly> moves = new ArrayList<>();
|
||||
|
||||
for (int r = 0; r < size; r++) {
|
||||
for (int c = 0; c < size; c++) {
|
||||
if (getCell(r, c) == null) {
|
||||
moves.add(new HexPly(me, r, c));
|
||||
}
|
||||
}
|
||||
}
|
||||
return moves.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBoard safeCopy() {
|
||||
HexBoard copy = new HexBoard(this.size, this.getCurrentPlayer());
|
||||
for (int r = 0; r < size; r++) {
|
||||
System.arraycopy(this.cells[r], 0, copy.cells[r], 0, size);
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int r = 0; r < size; r++) {
|
||||
for (int k = 0; k < r; k++) sb.append(" ");
|
||||
for (int c = 0; c < size; c++) {
|
||||
Player p = getCell(r, c);
|
||||
char ch = '.';
|
||||
if (p == Player.PLAYER1) ch = '1';
|
||||
else if (p == Player.PLAYER2) ch = '2';
|
||||
sb.append(ch).append(" ");
|
||||
}
|
||||
sb.append("\n");
|
||||
}
|
||||
sb.append("Current player: ").append(getCurrentPlayer()).append("\n");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
40
javaAPI/fr/iut_fbleau/HexGame/HexMain.java
Normal file
40
javaAPI/fr/iut_fbleau/HexGame/HexMain.java
Normal file
@@ -0,0 +1,40 @@
|
||||
package fr.iut_fbleau.HexGame;
|
||||
|
||||
import fr.iut_fbleau.GameAPI.*;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Lancement d'une partie de Hex en console.
|
||||
*/
|
||||
public class HexMain {
|
||||
|
||||
public static void main(String[] args) {
|
||||
int size = 7;
|
||||
if (args.length >= 1) {
|
||||
try { size = Integer.parseInt(args[0]); } catch (NumberFormatException ignored) {}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
System.out.println(board);
|
||||
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
|
||||
|
||||
sc.close();
|
||||
}
|
||||
}
|
||||
31
javaAPI/fr/iut_fbleau/HexGame/HexPly.java
Normal file
31
javaAPI/fr/iut_fbleau/HexGame/HexPly.java
Normal file
@@ -0,0 +1,31 @@
|
||||
package fr.iut_fbleau.HexGame;
|
||||
|
||||
import fr.iut_fbleau.GameAPI.*;
|
||||
|
||||
/**
|
||||
* Représente un coup dans le jeu de Hex.
|
||||
*/
|
||||
public class HexPly extends AbstractPly {
|
||||
|
||||
private final int row;
|
||||
private final int col;
|
||||
|
||||
public HexPly(Player j, int row, int col) {
|
||||
super(j);
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
public int getRow() {
|
||||
return this.row;
|
||||
}
|
||||
|
||||
public int getCol() {
|
||||
return this.col;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "HexPly{player=" + getPlayer() + ", row=" + row + ", col=" + col + "}";
|
||||
}
|
||||
}
|
||||
68
javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
Normal file
68
javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package fr.iut_fbleau.HexGame;
|
||||
|
||||
import fr.iut_fbleau.GameAPI.*;
|
||||
|
||||
import java.util.Scanner;
|
||||
|
||||
/**
|
||||
* Joueur humain en console.
|
||||
*
|
||||
* Format attendu : "row col" (indices à partir de 0).
|
||||
*/
|
||||
public class HumanConsolePlayer extends AbstractGamePlayer {
|
||||
|
||||
private final Scanner in;
|
||||
|
||||
public HumanConsolePlayer(Player me, Scanner in) {
|
||||
super(me);
|
||||
this.in = in;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AbstractPly giveYourMove(IBoard board) {
|
||||
if (!(board instanceof HexBoard)) {
|
||||
throw new IllegalArgumentException("Ce joueur attend un HexBoard.");
|
||||
}
|
||||
HexBoard hb = (HexBoard) board;
|
||||
|
||||
while (true) {
|
||||
System.out.println(hb);
|
||||
System.out.print("Joueur " + board.getCurrentPlayer() + " - entrez un coup (row col) : ");
|
||||
|
||||
String line = in.nextLine().trim();
|
||||
if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
|
||||
throw new IllegalStateException("Partie interrompue par l'utilisateur.");
|
||||
}
|
||||
if (line.equalsIgnoreCase("help")) {
|
||||
System.out.println("Entrez deux entiers : row col (0 <= row,col < " + hb.getSize() + ")");
|
||||
System.out.println("Commandes: help, quit");
|
||||
continue;
|
||||
}
|
||||
|
||||
String[] parts = line.split("\\s+");
|
||||
if (parts.length != 2) {
|
||||
System.out.println("Format invalide. Exemple: 3 4");
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
int r = Integer.parseInt(parts[0]);
|
||||
int c = Integer.parseInt(parts[1]);
|
||||
HexPly ply = new HexPly(board.getCurrentPlayer(), r, c);
|
||||
|
||||
if (!hb.isLegal(ply)) {
|
||||
System.out.println("Coup illégal (case occupée / hors plateau / mauvais joueur). Réessayez.");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (hb.isWinningMove(ply)) {
|
||||
System.out.println("Coup gagnant !");
|
||||
}
|
||||
|
||||
return ply;
|
||||
} catch (NumberFormatException e) {
|
||||
System.out.println("Veuillez entrer deux entiers.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
271
javaAPI/fr/iut_fbleau/HexGame/Simulation.java
Normal file
271
javaAPI/fr/iut_fbleau/HexGame/Simulation.java
Normal file
@@ -0,0 +1,271 @@
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user