Commit 5a013c2d authored by Timothy LAIRD's avatar Timothy LAIRD

Merge commit

parent 90e81581
......@@ -5,7 +5,7 @@
<url>src/tictactoecodingame/AlgoRechercheMCTS.java</url>
<bookmark id="1">
<name/>
<line>96</line>
<line>156</line>
<key/>
</bookmark>
</file>
......@@ -14,7 +14,7 @@
<group>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/ArbreMCTS.java</file>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/Node.java</file>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/Fraction.java</file>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/Arbitre.java</file>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/AlgoRechercheMCTS.java</file>
<file>file:/C:/Users/timot/OneDrive/Desktop/2019-2020/Projet-Informatique/TicTacToe/src/tictactoecodingame/Player.java</file>
</group>
......
......@@ -10,137 +10,349 @@ import java.util.Iterator;
import java.util.Random;
/**
*
* @author timot
* <p>Après implémentation et tests de l'algorithme MCTS, il est intéressant de remarquer que cette implémentation de l'algorithme
* ne joue pas "parfaitement". En effet, celà se remarque surtout dans le Tic Tac Toe 3x3, que notre algorithme jouera toujours
* comme premier coup le centre, même quand il a résolu le jeu (voir si dessous).
* Ce coup est en effet celui qui admet le plus grand taux de victoire, mais n'est pas le meilleur. En effet, ouvrir dans un coin permets
* de garantir une victoire où une égalité. Une faiblesse de notre implémentation est liée au fait que le meilleur coup en terme de
* taux de victoire n'est pas nécessairement le véritable "meilleur coup".</p>
* <p>Cette classe est le coeur de l'implémentation de l'algorithme MCTS. Elle contient donc une certaine quantité de données :</p>
* <ul>
* <li>Une instance d'arbre : Avant tout, l'algorithme a besoin d'un arbre pour pouvoir fonctionner.</li>
* <li>Un entier : Cet entier représente le nombre d'itérations de l'algorithme qui sera faite à chaque appel de meilleurCoup.</li>
* <li>Un coefficient réel : Cette valeur corresponds au coefficient d'exploration dans la formule de L'Upper Confidence Bound applied to Trees.
* Sa valeur standard est sqrt(2).</li>
* <li>Un générateur de nombre aléatoires : Ce générateur est nécessaire pour simuler aléatoirement des parties. Une amélioration qui peut être
* apportée à l'algorithme est d'utiliser une heuristique pour simuler les parties plus intelligement.</li>
* <li>La liste de Nodes chemin : Cette liste permet de tracer les différents noeuds qui ont étés suivis à chaque itération de l'algorithme.
* Celà n'est pas strictement nécessaire, mais simplifie grandement le travail de mise à jour de l'arbre après la simulation.</li>
* <li>Un booléen : Cet attribut, ainsi que tous ceux qui suivent, ne font pas partie de l'implémentation de base de l'algorithme. Ces
* données sont utiles à la mise en place d'un système de cache, permettant à l'algorithme de retenir les données traitées dans une partie
* pour les prochaines. Cela amène entre autre à la résolution du Tic Tac Toe 3x3, avec suffisamment de parties.
* <strong>ATTENTION : cette fonctionnalité est à éviter pour un grand nombre de simulations de parties 9x9, car la consommation en
* mémoire est importante.</strong></li>
* <li>Le Node firstRoot : Ce node permets de sauvegarder la racine qui corresponds à un jeu vide entre différentes parties, étant donné que la
* racine de l'arbre en soi est modifiée dans notre implémentation. Ce Node pointe à la même adresse mémoire que le Node à l'initialisation
* de l'arbre, il est donc mis à jour quand ce dernier l'est.</li>
* <li>Un tableau de Node : Ce tableau stocke, au même titre que <strong>firstRoot</strong>, les calculs déjà faits par l'algorithme. Celui ci prends
* la forme d'un tableau car il stocke les différents arbres après que l'adversaire aie joué son premier coup. Le tableau a les mêmes
* dimensions que la grille de jeu. Cette donnée n'est pas strictement nécessaire. En effet, une optimisation de l'algorithme permettrait de
* réutiliser <strong>firstRoot</strong>, car les données sont les mêmes, avec les joueurs inversés. Cela offrirait une division d'espace
* mémoire utilisé par cette fonctionnalité de 2, ainsi qu'une augmentation de remplissage de ces informations de 2 (ca se voit facilement
* avec le Tic Tac Toe 3x3 : le jeu est résolu 2 fois).</li>
* <li>Le Node trueRoot : Ce Node est utile pour la génération du cache partie après partie. Il pointe vers le Node avec lequel il a commencé
* la partie, nous permettant de mettre à jour l'intégralité de l'arbre, même en étant profondément dedans. Cet attribut ne serait pas
* nécessaire avec l'optimisation décrite précédemment.</li>
* <li>La liste de node cachedChemin : Cette liste corresponds au chemin "invisible" à effectuer dans la mise à jour de l'arbre,
* dans le cas où on veut générer un cache. En effet, en temps normal, l'algorithme descends dans l'arbre de jeu, et ne mets à jour que
* les informations utiles, c'est à dire celles directement en dessous. Mais si on veut générer un cache correct, il faut systématiquement
* mettre à jour l'intégralité de l'arbre.</li>
* </ul>
*/
public class AlgoRechercheMCTS extends AlgoRecherche {
private final ArbreMCTS search;
private final int maxIterations;
private final Random seed;
private ArrayList<Integer> chemin;
private final double coeff;
private final Random seed;
private ArrayList<Node> chemin;
private final boolean cache;
private final Node firstRoot;
private final Node[][] rootTable;
private Node trueRoot;
private ArrayList<Node> cachedChemin;
public AlgoRechercheMCTS(Joueur player, Joueur opponent, int m, double c){
/**
* <div>Initialisation d'une instance de l'algorithme.</div>
* @param player Le joueur ordi jouant avec cette instance de l'algorithme.
* @param opponent Son adversaire.
* @param m Le nombre d'itérations à effectuer par recherche de coup.
* @param c Le coefficient à utiliser dans L'UCT.
* @param line La longueur des lignes du plateau de jeu (utile pour cette implémentation du cache uniquement).
* @param column La longueur des colonnes du plateau de jeu (utile pour cette implémentation du cache uniquement).
* @param storeCache Le booléen indiquant si l'utilisateur veut que l'algorithme utilise un cache.
*/
public AlgoRechercheMCTS(Joueur player, Joueur opponent, int m, double c, int line, int column, boolean storeCache){
search = new ArbreMCTS(player, opponent);
maxIterations = m;
seed = new Random();
coeff = c;
firstRoot = search.root();
rootTable = new Node[line][column];
cache = storeCache;
}
@Override
public Coup meilleurCoup(Plateau _plateau, Joueur _joueur, boolean _ponder) {
Coup lastPlayed = _plateau.getDernierCoup();
if(search.root().coup() == null){
search.root().coup(lastPlayed);
expansion(search.root(), _plateau);
}
Node tmp = null;
Iterator<Node> children = search.root().children().iterator();
if(lastPlayed != null){
while(children.hasNext()){
tmp = children.next();
if(tmp.coup().equals(lastPlayed)){
search.root(tmp);
/*
Le début de cette méthode sert deux fonctions : Si on est en plein milieu d'une partie, il prends en compte le coup adverse et
descends dans l'arbre en conséquence (lorsqu'on compte au moins 2 jetons dans la grille, on est "en plein milieu d'une partie").
Si on utilise la même instance de l'algorithme pour jouer plusieurs parties, il se remets à zéro (lorsqu'on a au plus 1 jeton dans
la grille). Si l'utilisateur demande un cache, l'algorithme n'oublie pas ses données, sinon oui. Dans le cas où l'algorithme compte
0 jetons, il déduit que c'est à lui de commencer, sinon qu'il joue en 2eme.
*/
//Compte du nombre de jetons dans la grille, s'arrêtant à 2 (pas besoin d'en savoir plus).
CoupTicTacToe lastPlayed = (CoupTicTacToe) _plateau.getDernierCoup(); Node tmp;
Iterator<Node> children;
int nbJeton = 0;
for(int i = 0; i < _plateau.getNbLignes(); i++){
for(int j = 0; j < _plateau.getNbColonnes(); j++){
if(_plateau.getPiece(new Case(j, i)) != null){
nbJeton++;
if(nbJeton > 1){
break;
}
}
}
}
switch(nbJeton){
case 0:
if(cache){
//Appel du cache déjà existant et réinitialisation du chemin en cache
search.root(firstRoot);
trueRoot = firstRoot;
cachedChemin = new ArrayList<>();
}else{
//Remise à zéro de l'algorithme
Joueur opponent;
if(search.root().player() == _joueur){
opponent = search.root().opponent();
}else{
opponent = search.root().player();
}
search.root(new Node(null, _joueur, opponent));
}
break;
case 1:
if(cache){
//Appel du cache déjà existant et réinitialisation du chemin en cache
int i = lastPlayed.getLigne();
int j = lastPlayed.getColonne();
if(rootTable[i][j] == null){
Joueur opponent;
if(search.root().player() == _joueur){
opponent = search.root().opponent();
}else{
opponent = search.root().player();
}
rootTable[i][j] = new Node(lastPlayed, _joueur, opponent);
}
search.root(rootTable[i][j]);
trueRoot = rootTable[i][j];
cachedChemin = new ArrayList<>();
}else{
//Remise à zéro de l'algorithme
Joueur opponent;
if(search.root().player() == _joueur){
opponent = search.root().opponent();
}else{
opponent = search.root().player();
}
search.root(new Node(lastPlayed, _joueur, opponent));
}
break;
default:
//Descente dans l'arbre suivant le coup adverse.
children = search.root().children().iterator();
while(children.hasNext()){
tmp = children.next();
if(tmp.coup().equals(lastPlayed)){
search.root(tmp);
//Ralonge du chemin en cache avec le Node représentant le coup adverse, si demandé.
if(cache){
cachedChemin.add(tmp);
}
break;
}
}
}
//Initialisation de l'algorithme.
Node root = search.root();
Node nextNode;Joueur winner;
_plateau.sauvegardePosition(0);
int iterations = 0;
int trivial = 0;
while(iterations < maxIterations){
iterations++;
chemin = new ArrayList<>();
Node nextNode = selection(root, _plateau);
if(!_plateau.partieTerminee()){
expansion(nextNode, _plateau);
trivial++;
}
if(!nextNode.children().isEmpty()){
nextNode = nextNode.children().get(seed.nextInt(nextNode.children().size()));
_plateau.joueCoup(nextNode.coup());
if(root.solved()){
/*
Cas extrême, qui ne s'applique que au 3x3 : Si la racine de l'arbre est résolue, soit l'intégralité du jeu, l'algorithme
ne s'exécute plus.
*/
break;
}else{
/*
Si l'utilisateur demande un cache, le chemin de mise à jour dans chaque itération
commence toujours par le chemin en cache. Sinon il est vidé.
*/
chemin = new ArrayList<>();
if(cache){
chemin.addAll(cachedChemin);
}else{
chemin.add(root);
}
/*
Sélection de l'algorithme. La valeur null est une valeur particulière renvoyée intentionellement,
celà est expliquée dans la fonction en question
*/
nextNode = selection(root, _plateau);
if(nextNode == null){
_plateau.restaurePosition(0);
}else{
//Si la sélection ne renvoie pas un null, l'itération compte comme une vraie itération de l'algorithme.
iterations++;
//L'expansion est effectuée avec la simulation, à cette étape.
winner = simulate(nextNode, _plateau);
/*
Si l'utilisateur demande un cache, le retour est fait sur l'intégralité de l'arbre initial. Sinon, seulement sur le
sous arbre en cours.
*/
if(cache){
backPropagation(winner, trueRoot);
}else{
backPropagation(winner, root);
}
//Remise à zéro du plateau à la fin d'une itération.
_plateau.restaurePosition(0);
}
}
Joueur winner = simulate(nextNode, _plateau);
backPropagation(root, winner);
_plateau.restaurePosition(0);
}
Node nextPlay = root.nextPlay();
//Sélection du noeud optimal
Node nextPlay = root.nextPlay();
//Ralonge du chemin en cache avec le Node représentant notre coup, si demandé.
if(cache){
cachedChemin.add(nextPlay);
}
search.root(nextPlay);
return nextPlay.coup();
}
public Node selection(Node n, Plateau pl){
/**
* <div>Cette méthode exécute la partie sélection de l'algorithme.</div>
* @param n Le noeud père dont on va sélectionner un des fils.
* @param pl Le plateau représentant l'état du jeu en arrivant sur le noeud père. Il sera mis à jour après la sélection d'un fils.
* @return Le Node sélectionné
*/
private Node selection(Node n, Plateau pl){
//Condition d'arrêt de la récursivité.
if (n.children().isEmpty()){
return n;
}
else {
//Initialisation de la sélection.
double valMax=Double.NEGATIVE_INFINITY;
int indiceSelection=0;
ArrayList<Node> f = n.children();
double val;
for(int i = 0; i < f.size(); i++){
Node nf = f.get(i);
if(nf.visits() == 0){
val = Double.MAX_VALUE;
}else{
val = (nf.wins()/nf.visits())+coeff*Math.sqrt(Math.log(n.visits())/nf.visits());
}
if (val>valMax){
indiceSelection=i;
valMax=val;
double val;boolean allSolved = true;
Node selection = null;
Iterator<Node> nodes = n.children().iterator();
while(nodes.hasNext()){
Node nf = nodes.next();
//La sélection ne considèrera que les noeuds non-résolus.
if(!(nf.solved())){
allSolved = false;
if(nf.visits() == 0){
/*
Si le noeud est à 0 visites, on force l'algorithme à le visiter. Ce forcage est nécessaire au bon fonctionnement
de l'algorithme, dans notre implémentation, si on veut que l'algorithme fonctionne comme voulu.
*/
val = Double.MAX_VALUE;
}else{
//Cas par défaut : Application de l'UCT
val = nf.winrate()+coeff*Math.sqrt(Math.log(n.visits())/nf.visits());
}
if (val>valMax){
selection=nf;
valMax=val;
}
}
}
Node noeudSelectionne=f.get(indiceSelection);
pl.joueCoup(noeudSelectionne.coup());
chemin.add(indiceSelection);
return selection(noeudSelectionne, pl);
if(allSolved){
/*
Cette condition apparait si tous les fils de n sont résolus (voir Node.java pour définition). On mets à jour n et on
saute l'itération de l'algorithme en cours.
*/
n.solved(true);
return null;
}else{
//Cas standard : mise à jour du plateau et récursion de l'algorithme.
pl.joueCoup(selection.coup());
chemin.add(selection);
return selection(selection, pl);
}
}
}
/**
* <div>Cette méthode traite l'extension d'une feuille, en fonction des coups disponibles.</div>
* @param leaf Le noeud à étendre.
* @param leafPlateau Le plateau représentant l'état du jeu en arrivant sur le noeud leaf.
*/
private void expansion(Node leaf, Plateau leafPlateau){
ArrayList<Coup> coups = leafPlateau.getListeCoups(leaf.player());
Iterator<Coup> coup = coups.iterator();
Coup currentCoup;
while(coup.hasNext()){
//Ajout de chaque fils correspondant à un des coups possible à partir du Node
currentCoup = coup.next();
Node newLeaf = new Node(currentCoup, leaf.opponent(), leaf.player());
leaf.children().add(newLeaf);
}
}
/**
* <div>Cette méthode simule au hasard le déroulement d'une partie. Il est pertinent de remarquer que l'extension se fait pendant
* la simulation. Celà implique entre autre que tous les états de jeu rencontrés pendant la simulation sont ajoutés à l'arbre et
* étendus. Celà n'est pas conforme à l'énoncé théorique de l'algorithme, mais préférable dans cette implémentation, surtout dans
* le cadre de l'utilisation du cache. En effet, l'algorithme théorique a tendance à compter certaine victoires en double
* (1 victoire par noeud étendu, pour être exact). Cette implémentation remédie à ce problème, et permets d'avoir un cache représentant
* exactement le jeu (nous avons testé cette affirmation pour le 3x3, et retrouvé les valeurs issues de la résolution du jeu).</div>
* @param node Le noeud à partir duquel simuler.
* @param board Le plateau représentant l'état du jeu en arrivant dans ce noeud.
* @return L'objet Joueur gagnant cette simulation.
*/
private Joueur simulate(Node node, Plateau board){
Joueur p1 = node.player();Joueur p2 = node.opponent();
Joueur currentPlayer = node.player();
Coup coup;
ArrayList<Coup> coups;
while(!board.partieTerminee()){
coups = board.getListeCoups(currentPlayer);
coup = coups.get(seed.nextInt(coups.size()));
board.joueCoup(coup);
if(currentPlayer.equals(p1)){
currentPlayer = p2;
}else{
currentPlayer = p1;
}
if(!board.partieTerminee()){
//Extension du noeud, choix d'un fils au hasard, mise à jour du plateau et récursion de l'algorithme.
expansion(node, board);
int index = seed.nextInt(node.children().size());
Node nextMove = node.children().get(index);
chemin.add(nextMove);
board.joueCoup(nextMove.coup());
return simulate(nextMove, board);
}else{
//Condition d'arrêt de la récursion
return board.vainqueur();
}
return board.vainqueur();
}
public void backPropagation(Node n, Joueur gagnant){
Node currentNode = n; int index = 0;
do{
/**
* <div>Cette méthode gère la mise à jour des notes de l'arbre.</div>
* @param gagnant Le joueur gagnant de la simulation effectué précédemment.
* @param n Le noeud à partir duquel mettre à jour (n est toujours soit la racine relative de l'arbre, soit sa racine absolue).
*/
private void backPropagation(Joueur gagnant, Node n){
Node currentNode = n;
Iterator<Node> followChemin = chemin.iterator();
//Distinction légère entre les égalités et les victoires : tout le monde marque une égalité si elle arrive
if(gagnant == null){
currentNode.addVisit();
if (currentNode.player().equals(gagnant)){
currentNode.addDraw();
while(followChemin.hasNext()){
currentNode = followChemin.next();
currentNode.addVisit();
currentNode.addDraw();
}
}else{
currentNode.addVisit();
if (currentNode.opponent().equals(gagnant)){
currentNode.addWin();
}
while(followChemin.hasNext()){
currentNode = followChemin.next();
currentNode.addVisit();
if (currentNode.opponent().equals(gagnant)){
currentNode.addWin();
}
}
currentNode = currentNode.children().get(chemin.get(index));
index++;
}while(index < chemin.size());
currentNode.addVisit();
if (currentNode.player().equals(gagnant)){
currentNode.addWin();
}
//A la fin de l'algorithme, dans notre implémentation, currentNode est toujours une feuille représentant une partie finie, et est donc résolu.
currentNode.solved(true);
}
}
......@@ -61,16 +61,18 @@ public class Arbitre {
}
Joueur vainqueur = plateau.vainqueur();
/*
if ( vainqueur != null )
System.out.println( vainqueur + " gagne la partie ");
else
System.out.println( "Partie nulle ");
*/
return vainqueur;
}
public void startTournament( int _nbPartie , boolean _trace) {
public Joueur startTournament( int _nbPartie , boolean _trace) {
double[] nbVictoire = new double[2];
Joueur vainqueur;
......@@ -81,12 +83,12 @@ public class Arbitre {
for (int i = 0 ; i < _nbPartie ; i++ ) {
vainqueur = startNewGame(_trace);
if ( vainqueur == joueur1 ) nbVictoire[0]++;
if ( vainqueur == joueur1 ) nbVictoire[0]++;
if ( vainqueur == joueur2 ) nbVictoire[1]++;
if ( vainqueur == null ) {
nbVictoire[0]+=0.5;
nbVictoire[1]+=0.5;
nbVictoire[0]+=0.0;
nbVictoire[1]+=0.0;
}
if ( numJoueur == 0 ) {
......@@ -97,21 +99,27 @@ public class Arbitre {
currentJoueur = joueur1;
numJoueur=0;
}
System.out.println(joueur1 + " score : " + nbVictoire[0]);
System.out.println(joueur2 + " score : " + nbVictoire[1]);
//System.out.println(joueur1 + " score : " + nbVictoire[0]);
//System.out.println(joueur2 + " score : " + nbVictoire[1]);
}
System.out.println(joueur1 + " score : " + nbVictoire[0]);
System.out.println(joueur2 + " score : " + nbVictoire[1]);
if (nbVictoire[0] > nbVictoire[1])
System.out.println(joueur1 + " GAGNE ");
else
if (nbVictoire[1] > nbVictoire[0])
System.out.println(joueur2 + " GAGNE ");
else
System.out.println("Match nul");
if (nbVictoire[0] > nbVictoire[1]){
//System.out.println(joueur1 + " GAGNE ");
vainqueur = joueur1;
}else{
if (nbVictoire[1] > nbVictoire[0]){
//System.out.println(joueur2 + " GAGNE ");
vainqueur = joueur2;
}else{
//System.out.println("Match nul");
vainqueur = null;
}
}
return vainqueur;
}
public Joueur getCurrentJoueur() {
......
......@@ -7,11 +7,16 @@ package tictactoecodingame;
/**
*
* @author timot
* <div>Cette classe englobe la structure de noeud en un arbre, et permets d'agir sur sa racine.</div>
*/
public class ArbreMCTS {
private Node root;
/**
* <div>Initialisation d'un arbre : La racine représente un plateau vide.</div>
* @param pl Le joueur qui va jouer.
* @param o Le joueur qui vient de jouer.
*/
public ArbreMCTS(Joueur pl, Joueur o){
root = new Node(null, pl, o);
}
......
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
import java.util.ArrayList;
/**
*
* @author louis
*/
public class Fraction {
public static double frctValue(int parentVisit, int visits, int wins){
if(visits == 0){
return Integer.MAX_VALUE;
}
return (wins/visits) + 1.41 * Math.sqrt(Math.log(parentVisit) / visits);
}
public static Node bestChild(Node root){
int parentVisit = root.visits();
int maxIndex = 0;
double maxScore = 0;
ArrayList<Node> children = root.children();
double currentScore; Node currentNode;
for(int i = 0; i < children.size(); i++){
currentNode = children.get(i);
currentScore = frctValue(parentVisit, currentNode.visits(), currentNode.wins());
if(currentScore > maxScore){
maxScore = currentScore;
maxIndex = i;
}
}
return children.get(maxIndex);
}
}
......@@ -9,16 +9,35 @@ import java.util.ArrayList;
import java.util.Iterator;
/**
*
* @author timot
* <div>Cette classe instancie les noeuds d'un arbre. Chaque noeud contient un certain nombre d'informations :</div>
* <ul>
* <li>Un coup : Ce coup représente le coup qui <strong>vient</strong> d'être joué.</li>
* <li>Deux joueurs : Ces joueurs représentent respectivement le joueur qui va jouer, et son adversaire, soit le joueur qui vient
* de jouer.</li>
* <li>Trois entiers : Ces entiers comptent le nombre de victoires, égalités et nombre total de parties jouées passant par ce noeud.
* Les victoires sont comptées pour le joueur qui vient de jouer, c'est à dire opponent, étant donné que ces valeurs sont uniquement
* lues depuis le contexte de leur parent dans l'arbre, où opponent devient player.</li>
* <li>Une liste de Node : Cette liste représente simplement les enfants du noeud.</li>
* <li>Un booléen : Ce booléen indique si le noeud est résolu. Un noeud résolu est un noeud représentant une partie terminée, ou un
* noeud dont tous les fils sont résolus.</li>
* </ul>
*/
public class Node {
private Coup coup;
private final Coup coup;
private final Joueur player;
private final Joueur opponent;
private int visits = 0;
private int wins = 0;
private int draws = 0;
private final ArrayList<Node> children;
private boolean solved = false;
/**
* <div>Initialisation standard d'un Node</div>
* @param c Le coup qui vient d'être joué.
* @param pl Le joueur qui va jouer le prochain coup.
* @param o Le joueur qui vient de jouer le coup c.
*/
public Node(Coup c, Joueur pl, Joueur o){
coup = c;
player = pl;
......@@ -26,6 +45,7 @@ public class Node {
children = new ArrayList<>();
}
//Les accesseurs et modifieurs nécessaires
public int visits(){
return visits;
}
......@@ -34,6 +54,10 @@ public class Node {
return wins;
}
public int draws(){
return draws;
}
public Joueur player(){
return player;
}
......@@ -50,26 +74,42 @@ public class Node {
return coup;
}
public void coup(Coup c){
coup = c;
public boolean solved(){
return solved;
}
public void solved(boolean s){
solved = s;
}
//Les fonctions permettant d'incrémenter le nombre de visites, victoires et égalités.
public void addVisit(){
visits++;
}
public void addWin(){
wins++;
wins ++;
}
public void addDraw(){
draws++;
}
/** <div>Une fonction permettant de calculer le taux de victoire d'un noeud.Ce taux peut prendre en compte les égalités, en fonction
du coefficient rentré.</div>
@return Le taux de victoire.
*/
public double winrate(){
if(visits == 0){
return 0;
}else{
return wins/visits;
return (wins + 0.3*draws)/visits;
}
}
/** <div>Une fonction permettant de trouver le Node fils avec le meilleur taux de victoire.</div>
* @return Le Node avec le meilleur taux de victoire de tous les fils de son père.
*/
public Node nextPlay(){
Node bestNode = null;
double winrate = Double.NEGATIVE_INFINITY;
......@@ -85,4 +125,10 @@ public class Node {
}
return bestNode;
}
//Une méthode permettant de visualiser les informations d'un noeud. Surtout utile pendant le débogage.
@Override
public String toString(){
return coup.toString() + " " + visits + " " + winrate() * 100;
}
}
......@@ -12,27 +12,54 @@ public class Player {
public static void main(String args[]) {
JoueurHumain humain = new JoueurHumain("Humain");
JoueurOrdi joueurOrdi = new JoueurOrdi("Ordi");
//JoueurHumain humain = new JoueurHumain("Humain");
//JoueurOrdi joueurOrdiRnd = new JoueurOrdi("OrdiRnd");
JoueurOrdi joueurOrdiMCTS1000 = new JoueurOrdi("OrdiMCTS1000");
JoueurOrdi joueurOrdiMCTS5000 = new JoueurOrdi("OrdiMCTS5000");
//JoueurOrdi joueurOrdiMCTS10000 = new JoueurOrdi("OrdiMCTS10000");
double coeff = Math.sqrt(2);
// Remplacer ici l'algorithme aléatoire par votre algorithme.
// Créer une nouvelle classe qui hérite de la class AlgoRecherche
AlgoRechercheMCTS alea = new AlgoRechercheMCTS(joueurOrdi, humain, 1000,0.1);
joueurOrdi.setAlgoRecherche(alea);
GrilleTicTacToe3x3 grille = new GrilleTicTacToe3x3();
Arbitre a = new Arbitre(grille, humain , joueurOrdi );
/*
AlgoRechercheAleatoire aleaRnd = new AlgoRechercheAleatoire();
joueurOrdiRnd.setAlgoRecherche(aleaRnd);
*/
AlgoRechercheMCTS mcts1000 = new AlgoRechercheMCTS(joueurOrdiMCTS1000, joueurOrdiMCTS5000, 1000, coeff, grille.getNbLignes(), grille.getNbColonnes(), true);
joueurOrdiMCTS1000.setAlgoRecherche(mcts1000);
AlgoRechercheMCTS mcts5000 = new AlgoRechercheMCTS(joueurOrdiMCTS5000, joueurOrdiMCTS1000, 5000, coeff, grille.getNbLignes(), grille.getNbColonnes(), true);
joueurOrdiMCTS5000.setAlgoRecherche(mcts5000);
/*
AlgoRechercheMCTS mcts10000 = new AlgoRechercheMCTS(joueurOrdiMCTS10000, joueurOrdiMCTS1000, 10000,coeff, grille.getNbLignes(), grille.getNbColonnes(), false);
joueurOrdiMCTS10000.setAlgoRecherche(mcts10000);
*/
// Arbitre a = new Arbitre(grille, joueurOrdi , humain );
a.startNewGame(true); // Demarre une partie en affichant la grille du jeu
// Pour lancer un tournooi de 100 parties en affichant la grille du jeu
//a.startTournament(1000 , false);
Arbitre a = new Arbitre(grille, joueurOrdiMCTS1000, joueurOrdiMCTS5000);
//a.startNewGame(true); // Demarre une partie en affichant la grille du jeu
// Pour lancer un tournoi de 100 parties en affichant la grille du jeu
int[] results = {0,0,0};Joueur winner;
for(int i = 0; i < 100; i++){
System.out.println("Round " + i);
winner = a.startTournament(100 , false);
if(winner != null){
if(winner.equals(joueurOrdiMCTS1000)){
results[0]++;
}else{
results[1]++;
}
}else{
results[2]++;
}
}
System.out.println();
System.out.println("1000 vs 5000");
System.out.println("Ordi MCTS 1000 :" + results[0]);
System.out.println("Ordi MCTS 5000 :" + results[1]);
System.out.println("Draws :" + results[2]);
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment