Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Groupe3-TicTacToe
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
BAHRMAN Louis
Groupe3-TicTacToe
Commits
8f445899
Commit
8f445899
authored
May 03, 2020
by
Timothy LAIRD
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Final commit 1
parent
0861ce2c
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
520 additions
and
163 deletions
+520
-163
AlgoRechercheMCTS.java
src/tictactoecodingame/AlgoRechercheMCTS.java
+358
-0
AlgoRechercheMinMax.java
src/tictactoecodingame/AlgoRechercheMinMax.java
+3
-7
ArbreMCTS.java
src/tictactoecodingame/ArbreMCTS.java
+13
-97
ArbreMinMax.java
src/tictactoecodingame/ArbreMinMax.java
+0
-1
GrilleTicTacToe9x9.java
src/tictactoecodingame/GrilleTicTacToe9x9.java
+7
-6
Joueur.java
src/tictactoecodingame/Joueur.java
+1
-0
Node.java
src/tictactoecodingame/Node.java
+134
-0
Player.java
src/tictactoecodingame/Player.java
+4
-3
Selection.java
src/tictactoecodingame/Selection.java
+0
-49
No files found.
src/tictactoecodingame/AlgoRechercheMCTS.java
0 → 100644
View file @
8f445899
/*
* 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
;
import
java.util.Iterator
;
import
java.util.Random
;
/**
* <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
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
;
/**
* <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
)
{
/*
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
;
while
(
iterations
<
maxIterations
){
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
);
}
}
}
//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
();
}
/**
* <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
;
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
;
}
}
}
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
){
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
();
}
}
/**
* <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
();
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
();
}
}
}
//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
);
}
}
src/tictactoecodingame/AlgoRechercheMinMax.java
View file @
8f445899
...
...
@@ -51,11 +51,7 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
return
depth
;
}
public
Joueur
[]
getPlayers
(
Joueur
joueur1
,
Joueur
joueur2
){
Joueur
[]
players
=
{
target
,
opponent
};
return
players
;
}
@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
plateau
=
_plateau
;
...
...
@@ -92,7 +88,6 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
plateau
.
sauvegardePosition
(
currentDepth
);
}
//On crée les nouveau noeuds à partir des coups disponible du point de vue du joueur à ce niveau de l'arbre
ArrayList
<
Coup
>
coups
=
plateau
.
getListeCoups
(
currentJoueur
);
if
(
plateau
.
partieTerminee
()){
Joueur
winner
=
plateau
.
vainqueur
();
if
(
winner
==
target
){
...
...
@@ -110,7 +105,8 @@ public class AlgoRechercheMinMax extends AlgoRecherche{
t
.
setvalue
(
c
);
}
else
{
ArrayList
<
ArbreMinMax
>
fils
=
new
ArrayList
<
ArbreMinMax
>();
ArrayList
<
Coup
>
coups
=
plateau
.
getListeCoups
(
currentJoueur
);
ArrayList
<
ArbreMinMax
>
fils
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
coups
.
size
();
i
++){
ArbreMinMax
a
=
new
ArbreMinMax
(
coups
.
get
(
i
));
if
(
currentJoueur
==
target
){
...
...
src/tictactoecodingame/ArbreMCTS.java
View file @
8f445899
...
...
@@ -5,111 +5,27 @@
*/
package
tictactoecodingame
;
import
java.util.ArrayList
;
/**
*
*
@author senda
*
<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
;
private
Fraction
value
;
private
ArrayList
<
Coup
>
coups
;
private
ArrayList
<
ArbreMCTS
>
fils
;
// Les constructeurs :
public
ArbreMCTS
(
Fraction
value
,
ArrayList
coups
,
ArrayList
fils
){
this
.
value
=
value
;
this
.
fils
=
fils
;
this
.
coups
=
coups
;
}
public
ArbreMCTS
(
int
den
,
int
num
,
ArrayList
coups
,
ArrayList
fils
){
this
.
value
.
den
=
den
;
this
.
value
.
num
=
num
;
this
.
fils
=
fils
;
this
.
coups
=
coups
;
}
public
ArbreMCTS
(
Fraction
value
,
Plateau
_plateau
,
Joueur
_joueur
){
this
.
value
=
value
;
this
.
coups
=
_plateau
.
getListeCoups
(
_joueur
)
;
int
a
=
coups
.
size
();
fils
=
new
ArrayList
();
for
(
int
i
=
0
;
i
<
a
;
i
++){
ArbreMCTS
Arbre_i
=
new
ArbreMCTS
(
new
Fraction
());
// Attention ! Ce constructeur initialise donc avec des valeurs nulles en racine !
fils
.
add
(
Arbre_i
);
}
}
public
ArbreMCTS
(
Fraction
value
){
this
.
value
=
value
;
}
public
ArbreMCTS
(
int
den
,
int
num
){
this
.
value
.
den
=
den
;
this
.
value
.
num
=
num
;
}
// Les accesseurs :
public
Fraction
getFraction
(){
return
value
;
}
public
double
getvalue
(){
return
(
value
.
getNote
());
}
public
ArrayList
<
ArbreMCTS
>
getfils
(){
return
(
fils
);
}
public
ArrayList
<
Coup
>
getcoups
(){
return
(
coups
);
}
public
Fraction
getFrac
(){
return
(
this
.
value
);
}
//Des choses sans nom :
public
void
setvalue
(
Fraction
value
){
this
.
value
=
value
;
}
public
void
setfils
(
ArrayList
fils
){
this
.
fils
=
fils
;
}
public
void
setcoups
(
ArrayList
coups
){
this
.
coups
=
coups
;
}
//Fonctions auxiliaires :
public
boolean
estFeuille
(){
return
(
fils
==
null
);
/**
* <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
);
}
public
boolean
estNoeud
(){
return
(
fils
!=
null
)
;
public
Node
root
(){
return
root
;
}
public
int
hauteur
(){
if
(
this
.
estFeuille
()){
return
1
;
}
else
{
int
a
=
this
.
fils
.
size
();
ArbreMCTS
fils0
=
this
.
fils
.
get
(
0
);
int
maxfils
=
fils0
.
hauteur
();
for
(
int
i
=
1
;
i
<
a
;
i
++){
ArbreMCTS
next
=
this
.
fils
.
get
(
i
);
maxfils
=
Math
.
max
(
next
.
hauteur
(),
maxfils
);
}
return
(
1
+
maxfils
);
}
public
void
root
(
Node
r
){
root
=
r
;
}
}
src/tictactoecodingame/ArbreMinMax.java
View file @
8f445899
...
...
@@ -6,7 +6,6 @@
package
tictactoecodingame
;
import
java.util.ArrayList
;
import
static
tictactoecodingame
.
Generator
.
random_tests
;
/**
*
...
...
src/tictactoecodingame/GrilleTicTacToe9x9.java
View file @
8f445899
...
...
@@ -21,9 +21,10 @@ public class GrilleTicTacToe9x9 extends Plateau {
int
nbPostionSauvegarde
;
CoupTicTacToe
dernierCoup
;
CoupTicTacToe
dernierCoupSav
;
CoupTicTacToe
[]
dernierCoupSav
=
new
CoupTicTacToe
[
100
]
;
Joueur
vainqueur
;
boolean
grilleGagnee
,
grilleGagneeSav
;
// vrai si dernier coup gagne une grille
boolean
grilleGagnee
;
boolean
[]
grilleGagneeSav
=
new
boolean
[
100
];
// vrai si dernier coup gagne une grille
@Override
public
void
init
()
{
...
...
@@ -296,8 +297,8 @@ public class GrilleTicTacToe9x9 extends Plateau {
grille3x3Sav
[
_index
][
c
][
l
]
=
grille3x3
[
c
][
l
];
}
}
dernierCoupSav
=
dernierCoup
;
grilleGagneeSav
=
grilleGagnee
;
dernierCoupSav
[
_index
]
=
dernierCoup
;
grilleGagneeSav
[
_index
]
=
grilleGagnee
;
}
...
...
@@ -317,8 +318,8 @@ public class GrilleTicTacToe9x9 extends Plateau {
}
}
vainqueur
=
null
;
dernierCoup
=
dernierCoupSav
;
grilleGagnee
=
grilleGagneeSav
;
dernierCoup
=
dernierCoupSav
[
_index
]
;
grilleGagnee
=
grilleGagneeSav
[
_index
]
;
}
private
boolean
isGrillePleine
()
{
...
...
src/tictactoecodingame/Joueur.java
View file @
8f445899
...
...
@@ -30,6 +30,7 @@ public abstract class Joueur {
}
abstract
public
Coup
joue
(
Plateau
p_plateau
);
@Override
public
String
toString
()
{
return
nom
;
}
...
...
src/tictactoecodingame/Node.java
0 → 100644
View file @
8f445899
/*
* 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
;
import
java.util.Iterator
;
/**
*
* <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
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
;
opponent
=
o
;
children
=
new
ArrayList
<>();
}
//Les accesseurs et modifieurs nécessaires
public
int
visits
(){
return
visits
;
}
public
int
wins
(){
return
wins
;
}
public
int
draws
(){
return
draws
;
}
public
Joueur
player
(){
return
player
;
}
public
Joueur
opponent
(){
return
opponent
;
}
public
ArrayList
<
Node
>
children
(){
return
children
;
}
public
Coup
coup
(){
return
coup
;
}
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
++;
}
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
+
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
;
Iterator
<
Node
>
child
=
children
.
iterator
();
Node
currentNode
;
double
currentWinrate
;
while
(
child
.
hasNext
()){
currentNode
=
child
.
next
();
currentWinrate
=
currentNode
.
winrate
();
if
(
currentWinrate
>
winrate
){
winrate
=
currentWinrate
;
bestNode
=
currentNode
;
}
}
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
;
}
}
src/tictactoecodingame/Player.java
View file @
8f445899
...
...
@@ -16,14 +16,15 @@ public class Player {
JoueurOrdi
joueurOrdi1
=
new
JoueurOrdi
(
"Ordi1"
);
JoueurOrdi
joueurOrdi2
=
new
JoueurOrdi
(
"Ordi2"
);
//Il faut deux joueurs car l'id fixe le jeton
GrilleTicTacToe9x9
grille
=
new
GrilleTicTacToe9x9
();
AlgoRechercheMinMax
minmax
=
new
AlgoRechercheMinMax
(
3
,
10
,
joueurOrdi1
,
joueurOrdi2
);
AlgoRechercheMCTS
mcts1000
=
new
AlgoRechercheMCTS
(
joueurOrdi2
,
joueurOrdi1
,
1000
,
Math
.
sqrt
(
2
),
grille
.
getNbLignes
(),
grille
.
getNbColonnes
(),
false
);
joueurOrdi1
.
setAlgoRecherche
(
minmax
);
joueurOrdi2
.
setAlgoRecherche
(
m
inmax
);
joueurOrdi2
.
setAlgoRecherche
(
m
cts1000
);
GrilleTicTacToe9x9
grille
=
new
GrilleTicTacToe9x9
();
Arbitre
a
=
new
Arbitre
(
grille
,
joueurOrdi1
,
joueurOrdi2
);
...
...
src/tictactoecodingame/Selection.java
deleted
100644 → 0
View file @
0861ce2c
/*
* 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
Selection
{
ArrayList
<
Integer
>
chemin
;
public
Selection
(){
chemin
=
new
ArrayList
<>();
}
public
Selection
(
double
c
,
ArbreMCTS
a
){
Selection
s
=
new
Selection
();
chemin
=
SelectionAux
(
c
,
a
,
s
,
0
).
chemin
;
}
private
Selection
SelectionAux
(
double
c
,
ArbreMCTS
a
,
Selection
s
,
int
nbSimulPere
){
if
(
a
.
estFeuille
()){
return
s
;
}
else
{
double
coefMax
=
0
;
int
indiceSelection
=
0
;
ArrayList
<
ArbreMCTS
>
f
=
a
.
getfils
();
for
(
int
i
=
0
;
i
<
f
.
size
();
i
++){
ArbreMCTS
arbreTeste
=
f
.
get
(
i
);
double
coef
=
arbreTeste
.
getvalue
()+
c
*
Math
.
sqrt
(
Math
.
log
(
nbSimulPere
)/
arbreTeste
.
getFraction
().
getDenominateur
());
if
(
coef
>
coefMax
){
indiceSelection
=
i
;
coefMax
=
coef
;
}
}
ArbreMCTS
arbreSelectionne
=
f
.
get
(
indiceSelection
);
int
nbSimulPereNew
=
arbreSelectionne
.
getFraction
().
getDenominateur
();
s
.
chemin
.
add
(
indiceSelection
);
return
SelectionAux
(
c
,
arbreSelectionne
,
s
,
nbSimulPereNew
);
}
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment