Conception et création d`un moteur d`échecs

publicité
PAUGAM Guillaume
LOHIER Théophile
Création d’un moteur d’échecs
Conception et création d’un moteur d’échecs
Sujet :
On cherche à développer un moteur d’échecs en C++, c’est-à-dire une intelligence artificielle capable
d’évaluer une position, et de jouer une partie entière.
L’interface graphique utilisée est Xboard / Winboard, et la communication avec celle-ci est assurée
par le protocole CECP (Chess Engine Communication Protocol).
Les algorithmes possibles à utiliser sont le minimax, l’élagage alpha-bêta, le negascout ou MTD-(f), et
on utilise une fonction d’évaluation.
Les structures de données sont :


Des Bitboards (tableaux de bits) pour les positions des pièces sur l’échiquier.
Un arbre de coups possibles.
Algorithmes :
L’idée générale est de considérer la position initiale comme un nœud, et l’ensemble des coups
possibles (et donc les positions résultantes) comme ses nœuds fils. Pour les coups suivants, il est
possible d’analyser tout l’arbre, cependant, le temps d’analyse croît exponentiellement avec la
profondeur. C’est pour cela qu’on choisit de ne pas explorer les branches qui ne semblent pas
prometteuses (on « élague » l’arbre).
Elagage alpha-bêta :
Cet algorithme est relativement facile à implémenter par rapport aux autres, puisqu’il n’utilise pas de
table de transposition. Il recalcule la valeur d’un nœud même si la position exacte a déjà été
rencontrée, car il ne les garde pas en mémoire.
Par conséquent, il est plus lourd à l’exécution, mais est moins encombrant en termes de stockage.
PAUGAM Guillaume
LOHIER Théophile
Création d’un moteur d’échecs
Pseudo-code :
fonction alphabeta( nœud, profondeur, α, β)
(* β représente le meilleur coup du joueur précédent *)
si profondeur = 0 ou nœud terminal
retourner eval(nœud)
pour chaque fils du noeud
α := max(α, -alphabeta(fils, profondeur -1, -β, -α))
si β≤α
break
(* coupure bêta*)
return α
(* appel initial *)
alphabeta(origine, profondeur, -infini, +infini)
On se base sur la symétrie max(a,b)= - min(-a,-b).
Algorithme Negascout :
Pour être efficace, cet algorithme a besoin de trier les fils du nœud. Cet ordre est déterminé par une
recherche peu profonde. Il suppose donc que le premier fils est la variation principale. Sans tri, cet
algorithme est inefficace car il réévalue des positions déjà rencontrées.
On s’attend en général à un gain de performance d’environ 10% par rapport à un élagage alpha-bêta
simple, tout en conservant les mêmes résultats. Il reste l’algorithme le plus utilisé pour les moteurs
d’échecs.
Pseudo-code :
fonction negascout(noeud, profondeur, α, β)
si noeud terminal ou profondeur = 0
return eval(noeud)
b := β
(* intervalle initial (-β, -α) *)
pour chaque fils
a := -negascout (fils, profondeur - 1, -b, -α)
si α < a < β et fils non premier fils (* vérifier intervalle non
nul *)
a := -negascout(fils, profondeur - 1, -β, -α) (* recherche
complète *)
α := max(α, a)
si α ≥ β
retourner α
(* coupure bêta *)
b := α + 1
(* nouvel intervalle nul *)
retourner α
PAUGAM Guillaume
LOHIER Théophile
Création d’un moteur d’échecs
MTD-(f) :
L’algorithme MTD-(f) (Memory-enhanced Test Driver) est l’algorithme le plus efficace car il gère les
tables de transposition et évalue par conséquent moins de nœuds. Il garde en mémoire toutes les
positions déjà rencontrées. Il est donc plus lourd et dépend fortement des tables de transpositions.
Pseudo-code :
fonction MTDF(racine, f, d)
g := f
b := +∞
a := -∞
tant que a < b
si g = a alors
β := g+1
sinon
β := g
g := AlphaBetaAvecMemoire(racine, β-1, β, d)
si g < β alors
b := g
sinon
a := g
retourner g
Fonction d’évaluation :
La fonction d’évaluation est de la forme :
Eval(nœud) = c1 * matériel + c2 * mobilité des pièces + c3 * control central + c4 * sécurité du roi + …
La priorité est de rechercher le gain de matériel (dont le roi) et de le nuancer par les autres
paramètres.
Heuristiques additionnelles possibles :
Heuristique à mouvement nul :
L’élagage alpha-bêta élimine normalement les branches où l’on trouve des « coupures », c’est-à-dire
des coups où la position résultante est tellement bonne pour le joueur ayant l’initiative que cela
implique que l’autre joueur n’a pas joué le meilleur coup.
Le but de l’heuristique est de faire passer le tour au joueur qui a normalement le trait (c’est-à-dire le
joueur qui doit jouer).Toutefois, s’il garde une position assez favorable pour générer des coupures,
alors la position courante génèrerait aussi probablement une coupure.
PAUGAM Guillaume
LOHIER Théophile
Création d’un moteur d’échecs
Au final, on fait passer le tour au joueur ayant trait (normalement interdit), et on effectue un élagage
alpha-bêta, mais de façon moins profonde. Cela permet d’élaguer une partie de l’arbre en trouvant
des coupures plus facilement, et donc d’accélérer l’exécution du logiciel.
Cependant, cette technique reste à utiliser dans des cas particuliers et avec modération, car il existe
des situations où l’heuristique pourrait faire faire au logiciel des erreurs critiques. Par exemple, la
situation du zugzwang, c’est-à-dire quand le joueur ne peut jouer aucun bon coup et préfèrerait
passer son tour, est un cas qui est mal géré par l’heuristique. Ainsi, il existe d’autres types de
positions où cette heuristique est très inefficace.
Heuristique à mémoire :
L’idée ici est de garder en mémoire des coups qui dans des positions précédentes étaient forts et de
les tester en premier (si légaux), car ils peuvent être bons. S’ils génèrent des coupures, on peut
même ne pas considérer les autres coups. Cette méthode est non optimale mais peut générer
d’assez bons résultats et est très légère à implémenter.
Téléchargement