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
99c68189
Commit
99c68189
authored
May 04, 2020
by
DELBECQ Théo
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'Theo' into 'master'
Amélioration et javadoc See merge request
!25
parents
781dba9b
3d8c9491
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 @
99c68189
...
...
@@ -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 @
99c68189
...
...
@@ -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 @
99c68189
...
...
@@ -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 @
99c68189
...
...
@@ -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 @
99c68189
/*
* 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 @
99c68189
...
...
@@ -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