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
3d8c9491
Commit
3d8c9491
authored
May 04, 2020
by
Theo Delbecq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Amélioration et javadoc
parent
af22f0f9
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
197 additions
and
28 deletions
+197
-28
AlgoRechercheMinMax.java
src/tictactoecodingame/AlgoRechercheMinMax.java
+61
-12
Arbitre.java
src/tictactoecodingame/Arbitre.java
+19
-0
ArbreMinMax.java
src/tictactoecodingame/ArbreMinMax.java
+14
-14
Generator.java
src/tictactoecodingame/Generator.java
+21
-1
MemoireMinMax.java
src/tictactoecodingame/MemoireMinMax.java
+79
-0
Player.java
src/tictactoecodingame/Player.java
+3
-1
No files found.
src/tictactoecodingame/AlgoRechercheMinMax.java
View file @
3d8c9491
...
...
@@ -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
{
...
...
src/tictactoecodingame/Arbitre.java
View file @
3d8c9491
...
...
@@ -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
]++;
...
...
src/tictactoecodingame/ArbreMinMax.java
View file @
3d8c9491
...
...
@@ -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
);
}
}
...
...
src/tictactoecodingame/Generator.java
View file @
3d8c9491
...
...
@@ -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
;
...
...
src/tictactoecodingame/MemoireMinMax.java
0 → 100644
View file @
3d8c9491
/*
* 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
;
}
}
}
src/tictactoecodingame/Player.java
View file @
3d8c9491
...
...
@@ -18,8 +18,10 @@ public class Player {
//Il faut deux joueurs car l'id fixe le jeton
GrilleTicTacToe9x9
grille
=
new
GrilleTicTacToe9x9
();
MemoireMinMax
.
setup
(
grille
);
AlgoRechercheMinMax
minmax
=
new
AlgoRechercheMinMax
(
3
,
10
,
joueurOrdi1
,
joueurOrdi2
);
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
);
...
...
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