Commit 3d8c9491 authored by Theo Delbecq's avatar Theo Delbecq

Amélioration et javadoc

parent af22f0f9
......@@ -8,8 +8,22 @@ package tictactoecodingame;
import java.util.ArrayList;
/**
*
* @author Théo
* <p>Il s'agit du corps principal de l'algorithme de recherche MinMax.</p><p>
* L'algorithme permet de renvoyer le meilleur coup à jouer pour un joueur dans
* une situation donnée selon l'algoritme du MinMax. </p><p>L'algorithme construit
* un arbre des suits de coups possibles à partir de la situation fournie. Cet arbre
* est construit sur une profondeur donnée et on a ensuite recours à Generator pour
* attribuer une certaine valeur à chaque coup représenté par les feuilles. Cette
* valeur est une estimation et permet d'éviter un besoin de ressource exponentiel.
* A chaque niveau de profondeur on remonte successivement la valeur, du minimum quand
* c'est au tour de l'adversaire ou du maximum quand c'est au tour du joueur cible.
* Chaque joueur essaye de maximiser son impact tout en minimisant celui de son adversaire.
* Un fois les valeurs remontée à la racine, on donne le coup avec le score le plus élévé
* qui semble donc être le meilleur à ce stade.</p><p>Une amélioratoin a été étudiée, elle
* permet de conserver une carte qui garde en mémoire la qualité des coups en terme de victoire.
* Cela permettrait de pondérer les coup donnés par l'algo MinMax pour avoir une estimation
* plus fine des bons coups grâce à l'apprentissage des parties successives.</p><p>
* Pour que cet algo fonctionne il doit être appelé uniquement s'il reste au moins une action à faire.</p>
*/
public class AlgoRechercheMinMax extends AlgoRecherche{
......@@ -21,15 +35,27 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
private Joueur target;
private Joueur opponent;
private int d_gen;
private boolean mem;
public AlgoRechercheMinMax(int depth, int d_gen, Joueur joueur1, Joueur joueur2){
/**
* <div>Le constructeur principal d'instance de l'algorithme</div>
* @param depth La profondeur d'étude déterminites des coups possibles
* @param d_gen Le nombre de tests al"atoires réalisés aux feuilles
* @param joueur1 Le premier Joueur, il faut garder en mémoire qui est le joueur cible, celui dont on souhaite maximiser l'impact
* @param joueur2 Son opposant, les deux joueur doivent être connus pour dubpliquer leur jetons
* @param mem Paramètre indiquant si l'amélioration de mémoire est active.
*/
public AlgoRechercheMinMax(int depth, int d_gen, Joueur joueur1, Joueur joueur2, boolean mem){
this.depth = depth;
this.d_gen = d_gen;
//Cet algo ne marche qu'avec des jeux à deux joueurs
target = joueur1;
opponent = joueur2;
this.mem = mem;
}
//Fonctions de réglage des paramètres
public void setRandGenDepth(int d_gen){
this.d_gen = d_gen;
}
......@@ -43,14 +69,22 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
opponent = joueur2;
}
public int getRandGenDepth(int d_gen){
public void setMem(boolean mem){
this.mem = mem;
}
public int getRandGenDepth(){
return d_gen;
}
public int getDepth(int depth){
public int getDepth(){
return depth;
}
public boolean getMem(){
return mem;
}
@Override
public Coup meilleurCoup( Plateau _plateau , Joueur _joueur , boolean _ponder ){
//On part du principe que la partie n'est pas terminée donc qu'il reste au moins un coup
......@@ -63,10 +97,19 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
ArbreMinMax explore = new ArbreMinMax();
builder(explore, target, 0);
explore.MinMax(0);
int m = Integer.MIN_VALUE;
//Si voulu, on pondère les coups
if(mem){
for(int i = 0; i < explore.getfils().size() ; i++){
double coup = explore.getfils().get(i).getvalue();
int ligne = ((CoupTicTacToe)explore.getfils().get(i).getcoup()).getLigne();
int colonne = ((CoupTicTacToe)explore.getfils().get(i).getcoup()).getColonne();
explore.getfils().get(i).setvalue(MemoireMinMax.eval(coup, ligne, colonne));
}
}
double m = Double.MIN_VALUE;
Coup c = null;
for(int i = 0; i < explore.getfils().size() ; i++){
int n = explore.getfils().get(i).getvalue();
double n = explore.getfils().get(i).getvalue();
if(n > m){
m = n;
c = explore.getfils().get(i).getcoup();
......@@ -76,13 +119,19 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
return c;
}
//Fonction auxiliaire récursive de création de l'arbre des coups possibles
/**
* <div>Fonction récursive de création et de parcours de l'abre des coups possibles</div>
* @param t L'arbre ou le sous-arbre parcouru
* @param currentJoueur Le joueur concerné à ce niveau de pronfondeur
* @param currentDepth Permet de savoir si on approche de la fin de partie et si on doit passer au remplissage des noeuds
*/
private void builder(ArbreMinMax t,Joueur currentJoueur, int currentDepth){
//On commence par mettre le plateau à jour en fonction du coup théorique joué
if(currentDepth == 0){
plateau.restaurePosition(0);
}
else{
//On parcours l'arbre en profondeur et on stocke toujours l'état du plateau en cours pour pouvoir naviquer rapidement entre les profondeurs
plateau.restaurePosition(currentDepth-1);
plateau.joueCoup(t.getcoup());
plateau.sauvegardePosition(currentDepth);
......@@ -91,17 +140,17 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
if(plateau.partieTerminee()){
Joueur winner = plateau.vainqueur();
if(winner == target){
t.setvalue(1);
t.setvalue(1.0);
}
else if(winner == opponent){
t.setvalue(-1);
t.setvalue(-1.0);
}
else {
t.setvalue(0);
t.setvalue(0.0);
}
}
else if (currentDepth == depth){
int c = Generator.random_tests(plateau, d_gen, target);
double c = (double)Generator.random_tests(plateau, d_gen, target);
t.setvalue(c);
}
else{
......
......@@ -71,6 +71,23 @@ public class Arbitre {
}
public void startTournament( int _nbPartie , boolean _trace) {
boolean mem = false;
if(joueur1 instanceof JoueurOrdi){
AlgoRecherche algo = ((JoueurOrdi)joueur1).getAlgoRecherche();
if (algo instanceof AlgoRechercheMinMax){
if(((AlgoRechercheMinMax) algo).getMem()){
mem = true;
}
}
}
if(joueur2 instanceof JoueurOrdi){
AlgoRecherche algo = ((JoueurOrdi)joueur2).getAlgoRecherche();
if (algo instanceof AlgoRechercheMinMax){
if(((AlgoRechercheMinMax) algo).getMem()){
mem = true;
}
}
}
double[] nbVictoire = new double[2];
Joueur vainqueur;
......@@ -81,6 +98,8 @@ public class Arbitre {
for (int i = 0 ; i < _nbPartie ; i++ ) {
vainqueur = startNewGame(_trace);
if(mem){MemoireMinMax.learn(plateau);}
if ( vainqueur == joueur1 ) nbVictoire[0]++;
if ( vainqueur == joueur2 ) nbVictoire[1]++;
......
......@@ -13,7 +13,7 @@ import java.util.ArrayList;
*/
public class ArbreMinMax {
protected int value;
protected double value;
//Coup théorique joué à ce noeud
protected Coup coup;
protected ArrayList<Coup> coups;
......@@ -28,14 +28,14 @@ public class ArbreMinMax {
this.coup = coup;
}
public ArbreMinMax(int value, ArrayList coups, ArrayList fils){
public ArbreMinMax(double value, ArrayList coups, ArrayList fils){
this.value = value;
this.fils = fils;
this.coups = coups;
}
public ArbreMinMax(int value, Plateau _plateau, Joueur _joueur){
public ArbreMinMax(double value, Plateau _plateau, Joueur _joueur){
this.value = value;
this.coups = _plateau.getListeCoups(_joueur) ;
int a = coups.size();
......@@ -47,13 +47,13 @@ public class ArbreMinMax {
}
public ArbreMinMax(int value){
public ArbreMinMax(double value){
this.value = value;
}
// Les accesseurs :
public int getvalue(){
public double getvalue(){
return(value);
}
......@@ -76,7 +76,7 @@ public class ArbreMinMax {
//Des choses sans nom :
public void setvalue(int value){
public void setvalue(double value){
this.value = value;
}
......@@ -113,11 +113,11 @@ public class ArbreMinMax {
}
}
public int Min(){
int m = Integer.MAX_VALUE;
public double Min(){
double m = Double.MAX_VALUE;
int a = this.getfils().size();
for(int i = 0; i < a ; i++){
int n = this.getfils().get(i).getvalue();
double n = this.getfils().get(i).getvalue();
if(n < m){
m = n;
}
......@@ -125,11 +125,11 @@ public class ArbreMinMax {
return m;
}
public int Max(){
int m = Integer.MIN_VALUE;
public double Max(){
double m = Double.MIN_VALUE;
int a = this.getfils().size();
for(int i = 0; i < a ; i++){
int n = this.getfils().get(i).getvalue();
double n = this.getfils().get(i).getvalue();
if(n > m){
m = n;
}
......@@ -148,12 +148,12 @@ public class ArbreMinMax {
}
if(c%2 == 0){
//On attribue le max
int m = this.Max();
double m = this.Max();
this.setvalue(m);
}
else{
//On attribue le min
int m = this.Min();
double m = this.Min();
this.setvalue(m);
}
}
......
......@@ -7,17 +7,36 @@ package tictactoecodingame;
/**
*
* @author Théo
* <p>La classe Generator n'a qu'une méthode qui permet d'explorer un certain nombre
* de fins de partie de manière aléatoire à partir d'un certain point et du poitn de
* vue d'un joueur donné.</p><p> La méthode renvoie un entier qui permet d'estimer le taux de
* victoire pour le joueur concerné à partir de ce point.</p><p> Pour chaque fin gagnante pour
* le joueur cible, la méthode compte +1, pour chque nul 0 et pour chaque défaite -1.
* C'est la somme de ces valeurs qui est renvoyée en sortie. Cette méthode s'appuie sur
* le code d'origine et notamment AlgoRechercheAleatoire. En effet, la méthode récupère un
* plateau en cours et simule deux joueurs IA ayant les même jetons que les joueurs concernés.
* On leur associe alors l'algo de recherche aleatoire et on laisse la partie se terminer.
* On relève le vainqueur pour ajuster le score et on remet le plateau au point d'étude
* avant de recommencer le nombre de fois demandé.</p>
*
*/
public class Generator {
/**
* <div>Méthode de tests aléatoires à répétition de la classe</div>
* @param plateau Le plateau en cours à partit duquel on débute les simulations.
* @param nb_tests Le nombre de tests aléatoires voulus
* @param target Le joueur de référence pour lequel on comtpe positivement les victoires.
*/
public static int random_tests(Plateau plateau, int nb_tests,Joueur target) {
int c = 0;
//Simulation de deux joueurs IA
JoueurOrdi player = new JoueurOrdi("player");
JoueurOrdi opponent = new JoueurOrdi("oppo");
AlgoRechercheAleatoire alea = new AlgoRechercheAleatoire( );
player.setAlgoRecherche(alea);
opponent.setAlgoRecherche(alea);
Joueur currentPlayer = player;
//On détermine qui du joueur cible où de son opposant à la main au début<.
int i=((CoupTicTacToe)plateau.getDernierCoup()).getJeton().getJoueur().getIdJoueur();
player.forceId(1-i);
opponent.forceId(i);
......@@ -45,6 +64,7 @@ public class Generator {
else if ( vainqueur.getIdJoueur() != target.getIdJoueur() )
c--;
}
//On utilise la postion 99 des sauvegardes car les premières emplacement sont souvent utilisés
plateau.restaurePosition(99);
}
return c;
......
/*
* 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;
/**
*
* <p>La classe MémoireMinMax permet de sauvegarder dans un tournoi une copie du plateau où
* chaque case reçoit un score en fonction des précédentes parties pour affiner le résulat des suivantes.</p>
* <p>Lorsqu'une partie est jouée toutes les cases qu'il occupe gagnent en poids tandis que celles
* occupées par le perdant voient leur note diminuer.</p><p>L'algorithme apprend au fur et a mesure des parties
* mais on ne peut pas l'utiliser dès le début, il faut beaucoup de données sinon la précision de la recherche risque dêtre
* dégradée par l'imprécision des coefficients.</p><p>Les coeffiencients sont multipliés à la valeur des coups tentés sur une
* case selon des reglès spécifique pour conserver l'ordre malgré les différences de signes.</p>
*/
public class MemoireMinMax {
private static int mode;
private static double[][] mem;
private static int seuil = 100;
private static int state = 0;
/**
* <div>Méthode permettant de régler la taille de la grille</div>
* @param grille Grille sur laquelle on joue
*/
public static void setup(Plateau grille){
mode = grille.getNbColonnes();
mem = new double[mode][mode];
}
public static void setSeuil(int s){
seuil = s;
}
public static int getSeuil(){
return seuil;
}
public static void learn(Plateau grille){
if(grille.partieNulle()){
return;
}
else{
//Le nombre de partie analysées est conservé pour garder des coefficients entre -1 et 1
state++;
for(int i=0; i<mode; i++){
for(int j=0; j<mode; i++){
Piece piece = grille.getPiece(new Case(j,i));
if(piece == null){
continue;
}
else if(piece.getJoueur()==grille.vainqueur()){
mem[i][j]=(mem[i][j]*(state-1.0)+1.0)/state;
}
else{
mem[i][j]=(mem[i][j]*(state-1.0)-1.0)/state;
}
}
}
}
}
public static double eval(double coup, int ligne, int colonne){
if(state < seuil){
return coup;
}
//En foctnion du signe la méthode d'application du coefficient diverge de manière à ne pas arriver à des prédictions contraires
double note = mem[ligne][colonne];
if(note*coup < 0){
return (1.0-Math.abs(note))*coup;
}
else{
return Math.abs(note)*coup;
}
}
}
......@@ -19,7 +19,9 @@ public class Player {
GrilleTicTacToe9x9 grille = new GrilleTicTacToe9x9();
AlgoRechercheMinMax minmax = new AlgoRechercheMinMax(3, 10, joueurOrdi1, joueurOrdi2);
MemoireMinMax.setup(grille);
AlgoRechercheMinMax minmax = new AlgoRechercheMinMax(3, 10, joueurOrdi1, joueurOrdi2, true);
AlgoRechercheMCTS mcts1000 = new AlgoRechercheMCTS(joueurOrdi2, joueurOrdi1, 1000, Math.sqrt(2), grille.getNbLignes(), grille.getNbColonnes(), false);
joueurOrdi1.setAlgoRecherche(minmax);
......
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