//Cet algo ne marche qu'avec des jeux à deux joueurs
//Cet algo ne marche qu'avec des jeux à deux joueurs où alterne les tours car on cher à maximiser l'impact d'un joueur cible un tour sur deux en minimisant l'impact de son opposant le reste du temps
target=joueur1;
target=joueur1;
opponent=joueur2;
opponent=joueur2;
this.mem=mem;
this.mem=mem;
...
@@ -87,13 +87,18 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
...
@@ -87,13 +87,18 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
//On part du principe que la partie n'est pas terminée donc qu'il reste au moins un coup
//On part du principe que la partie n'est pas terminée donc qu'il reste au moins un coup sans quoi la méthode ne peux fonctionner car la racine n'a pas de fils
plateau=_plateau;
plateau=_plateau;
plateau.sauvegardePosition(0);
plateau.sauvegardePosition(0);
//On détermine du point de vue de quel joueur on évalue la valeur du coup
if(target!=_joueur){
if(target!=_joueur){
opponent=target;
opponent=target;
target=_joueur;
target=_joueur;
}
}
/*
On construit l'arbre des coups récursivement avec une fonction auxiliaire. La même méthode évalue la valeur des feuilles
on les remonte avec l'algo du MinMax
*/
ArbreMinMaxexplore=newArbreMinMax();
ArbreMinMaxexplore=newArbreMinMax();
builder(explore,target,0);
builder(explore,target,0);
explore.MinMax(0);
explore.MinMax(0);
...
@@ -106,7 +111,8 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
...
@@ -106,7 +111,8 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
//On commence par mettre le plateau à jour en fonction du coup théorique joué
/*
A chaque appel récursif on arrive sur un nouveau noeud qui représente un coup. On ne conserve pas les parents donc on peut savoir les coups prcédent.
Cependant la méthode de parcours en profondeur préfixe implique que les parents on été visité avant et donc on a pu mémoriser les états de plateau correspondant.
On commence par mettre le plateau à jour en fonction du coup théorique joué
*/
if(currentDepth==0){
if(currentDepth==0){
plateau.restaurePosition(0);
plateau.restaurePosition(0);
}
}
...
@@ -136,8 +148,9 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
...
@@ -136,8 +148,9 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
plateau.joueCoup(t.getcoup());
plateau.joueCoup(t.getcoup());
plateau.sauvegardePosition(currentDepth);
plateau.sauvegardePosition(currentDepth);
}
}
//On crée les nouveau noeuds à partir des coups disponible du point de vue du joueur à ce niveau de l'arbre
//On crée les nouveau noeuds à partir des coups disponible du point de vue du joueur à ce niveau de l'arbre. Il y a 3 cas.
if(plateau.partieTerminee()){
if(plateau.partieTerminee()){
//Si la partie est terminée on est sur une feuille et on attribue directement une valeur au coup en fonction du vainqueur de la partie par rapport au joueur cible de l'algo
Joueurwinner=plateau.vainqueur();
Joueurwinner=plateau.vainqueur();
if(winner==target){
if(winner==target){
t.setvalue(1.0);
t.setvalue(1.0);
...
@@ -150,10 +163,17 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
...
@@ -150,10 +163,17 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
}
}
}
}
elseif(currentDepth==depth){
elseif(currentDepth==depth){
//Si on atteinte la profondeur d'évaluation demandée on arrete l'eapansion en profondeur et on crée une feuille auquel on associe une valeur estimée en testants des fin de partie aléatoires à partir de ce point
//La mémorisation ne se fait que si l'un des deux joueurs est une IA utilisant l'algo MinMax et que dans les tournois car il faut jouer plusieurs parties pour qu'elle soit utile
* 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
* 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
* 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>
* case selon des reglès spécifique pour conserver l'ordre malgré les différences de signes.</p>
* <p>Cette amélioration est incomptablie avec CodinGame dans la fafonc dont elle est implémentée dans la mesure
* où elle utilise plusieurs parties et apporte une modification à la classe arbitre pour pouvoir faire un apprentissage
* progressif dans les parties d'un tournoi.</p>
*/
*/
publicclassMemoireMinMax{
publicclassMemoireMinMax{
privatestaticintmode;
privatestaticintmode;
...
@@ -39,6 +42,12 @@ public class MemoireMinMax {
...
@@ -39,6 +42,12 @@ public class MemoireMinMax {
returnseuil;
returnseuil;
}
}
/**
* <div>Fonction d'apprentissage de la mémorisation. On sauvegarde les positions prises par le gagnant et le perdant pour alimenter les coefficients.
* Si la partie est nulle, elle n'apporte pas d'informations pour la mémorisation. Pour chaque case, on ajoute 1 pour chaque partie gagnée ou ce coup est joué
* par le gagnant et on retire 1 si ce coup est joué par le perdant. On divise par le total de parties mémorisées.</div>
* @param grille
*/
publicstaticvoidlearn(Plateaugrille){
publicstaticvoidlearn(Plateaugrille){
if(grille.partieNulle()){
if(grille.partieNulle()){
return;
return;
...
@@ -47,7 +56,7 @@ public class MemoireMinMax {
...
@@ -47,7 +56,7 @@ public class MemoireMinMax {
//Le nombre de partie analysées est conservé pour garder des coefficients entre -1 et 1
//Le nombre de partie analysées est conservé pour garder des coefficients entre -1 et 1
state++;
state++;
for(inti=0;i<mode;i++){
for(inti=0;i<mode;i++){
for(intj=0;j<mode;i++){
for(intj=0;j<mode;j++){
Piecepiece=grille.getPiece(newCase(j,i));
Piecepiece=grille.getPiece(newCase(j,i));
if(piece==null){
if(piece==null){
continue;
continue;
...
@@ -63,7 +72,15 @@ public class MemoireMinMax {
...
@@ -63,7 +72,15 @@ public class MemoireMinMax {
}
}
}
}
/**
* <div>Fonction d'application du coefficient. Cette fonction utilise la note mémorisée pour une case pour moduler la valeur d'un coup.</div>
//On n'utilise la mémoire des parties précédente qu'apèrs un certain nombre de partie jouées sans quoi les données qu'elle fournit ne sont pas significatives