202 lines
5.8 KiB
Prolog
202 lines
5.8 KiB
Prolog
/*
|
||
Ce programme met en oeuvre l'algorithme Minmax (avec convention
|
||
negamax) et l'illustre sur le jeu du TicTacToe (morpion 3x3)
|
||
*/
|
||
|
||
:- [tictactoe].
|
||
|
||
|
||
/****************************************************
|
||
ALGORITHME MINMAX avec convention NEGAMAX : negamax/5
|
||
*****************************************************/
|
||
|
||
/*
|
||
negamax(+J, +Etat, +P, +Pmax, [?Coup, ?Val])
|
||
|
||
SPECIFICATIONS :
|
||
|
||
retourne pour un joueur J donne, devant jouer dans
|
||
une situation donnee Etat, de profondeur donnee P,
|
||
le meilleur couple [Coup, Valeur] apres une analyse
|
||
pouvant aller jusqu'a la profondeur Pmax.
|
||
|
||
Il y a 3 cas a decrire (donc 3 clauses pour negamax/5)
|
||
|
||
1/ la profondeur maximale est atteinte : on ne peut pas
|
||
developper cet Etat ;
|
||
il n'y a donc pas de coup possible a jouer (Coup = rien)
|
||
et l'evaluation de Etat est faite par l'heuristique.
|
||
|
||
2/ la profondeur maximale n'est pas atteinte mais J ne
|
||
peut pas jouer ; au TicTacToe un joueur ne peut pas jouer
|
||
quand le tableau est complet (totalement instancie) ;
|
||
il n'y a pas de coup a jouer (Coup = rien)
|
||
et l'evaluation de Etat est faite par l'heuristique.
|
||
|
||
3/ la profondeur maxi n'est pas atteinte et J peut encore
|
||
jouer. Il faut evaluer le sous-arbre complet issu de Etat ;
|
||
|
||
- on determine d'abord la liste de tous les couples
|
||
[Coup_possible, Situation_suivante] via le predicat
|
||
successeurs/3 (deja fourni, voir plus bas).
|
||
|
||
- cette liste est passee a un predicat intermediaire :
|
||
loop_negamax/5, charge d'appliquer negamax sur chaque
|
||
Situation_suivante ; loop_negamax/5 retourne une liste de
|
||
couples [Coup_possible, Valeur]
|
||
|
||
- parmi cette liste, on garde le meilleur couple, c-a-d celui
|
||
qui a la plus petite valeur (cf. predicat meilleur/2);
|
||
soit [C1,V1] ce couple optimal. Le predicat meilleur/2
|
||
effectue cette selection.
|
||
|
||
- finalement le couple retourne par negamax est [Coup, V2]
|
||
avec : V2 is -V1 (cf. convention negamax vue en cours).
|
||
|
||
A FAIRE : ECRIRE ici les clauses de negamax/5
|
||
.....................................
|
||
*/
|
||
negamax(J,Etat,Pmax,Pmax,[nil,H]) :-
|
||
heuristique(J, Etat, H), !.
|
||
|
||
negamax(J, Etat, _, _, [nil, H]) :-
|
||
situation_terminale(J, Etat),
|
||
heuristique(J ,Etat, H), !.
|
||
|
||
negamax(J, Etat, P, Pmax, [Coup, V2]) :-
|
||
P < Pmax,
|
||
not(situation_terminale(J,Etat)),
|
||
successeurs(J, Etat, Succ),
|
||
loop_negamax(J, P, Pmax, Succ, Couples),
|
||
meilleur(Couples, [Coup, V1]),
|
||
V2 is -V1, !.
|
||
|
||
/*******************************************
|
||
DEVELOPPEMENT D'UNE SITUATION NON TERMINALE
|
||
successeurs/3
|
||
*******************************************/
|
||
|
||
/*
|
||
successeurs(+J,+Etat, ?Succ)
|
||
|
||
retourne la liste des couples [Coup, Etat_Suivant]
|
||
pour un joueur donne dans une situation donnee
|
||
*/
|
||
|
||
successeurs(J,Etat,Succ) :-
|
||
copy_term(Etat, Etat_Suiv),
|
||
findall([Coup,Etat_Suiv],
|
||
successeur(J,Etat_Suiv,Coup),
|
||
Succ).
|
||
|
||
/*************************************
|
||
Boucle permettant d'appliquer negamax
|
||
a chaque situation suivante :
|
||
*************************************/
|
||
|
||
/*
|
||
loop_negamax(+J,+P,+Pmax,+Successeurs,?Liste_Couples)
|
||
retourne la liste des couples [Coup, Valeur_Situation_Suivante]
|
||
a partir de la liste des couples [Coup, Situation_Suivante]
|
||
*/
|
||
|
||
loop_negamax(_,_, _ ,[], []).
|
||
loop_negamax(J,P,Pmax,[[Coup,Suiv]|Succ],[[Coup,Vsuiv]|Reste_Couples]) :-
|
||
loop_negamax(J,P,Pmax,Succ,Reste_Couples), %Récursion jusqu'à ce qu'il n'y ait plus de couple
|
||
adversaire(J,A), %Pour chaque couple, on récupère l'adversaire du joueur actuel,
|
||
Pnew is P+1, %On incrémente la profondeur courante,
|
||
negamax(A,Suiv,Pnew,Pmax, [_,Vsuiv]). %On relance négamax avec cette fois-ci le joueur adverse
|
||
% et la situation suivante (celle que l'on traite actuellement parmi les couples (Coup,Suiv)).
|
||
|
||
/*
|
||
|
||
A FAIRE : commenter chaque litteral de la 2eme clause de loop_negamax/5,
|
||
en particulier la forme du terme [_,Vsuiv] dans le dernier
|
||
litteral ?
|
||
*/
|
||
|
||
/*********************************
|
||
Selection du couple qui a la plus
|
||
petite valeur V
|
||
*********************************/
|
||
|
||
/*
|
||
meilleur(+Liste_de_Couples, ?Meilleur_Couple)
|
||
|
||
SPECIFICATIONS :
|
||
On suppose que chaque element de la liste est du type [C,V]
|
||
- le meilleur dans une liste a un seul element est cet element
|
||
- le meilleur dans une liste [X|L] avec L \= [], est obtenu en comparant
|
||
X et Y,le meilleur couple de L
|
||
Entre X et Y on garde celui qui a la petite valeur de V.
|
||
|
||
A FAIRE : ECRIRE ici les clauses de meilleur/2
|
||
*/
|
||
meilleur([X],X).
|
||
meilleur([[Coup, Val]|Rest], Bc) :-
|
||
meilleur(Rest, [BCoupTemp, BValTemp]),
|
||
|
||
(Val < BValTemp ->
|
||
Bc = [Coup,Val]
|
||
;
|
||
Bc = [BCoupTemp,BValTemp]
|
||
).
|
||
|
||
/******************
|
||
PROGRAMME PRINCIPAL
|
||
*******************/
|
||
|
||
main(B,V, Pmax) :-
|
||
situation_initiale(Situation),
|
||
joueur_initial(J),
|
||
negamax(J, Situation, 0, Pmax, [B, V]).
|
||
|
||
main(Pmax) :-
|
||
situation_initiale(Situation),
|
||
joueur_initial(J),
|
||
main_loop(J, Situation, Pmax).
|
||
|
||
|
||
main_loop(J, Situation, Pmax) :-
|
||
negamax(J, Situation, 0, Pmax, [B, V]),
|
||
writeln(B), writeln(V),
|
||
pretty_print(Situation),
|
||
successeur(J, Situation, B),
|
||
adversaire(J,A),
|
||
(B \= nil ->
|
||
main_loop(A, Situation, Pmax)
|
||
).
|
||
|
||
/*
|
||
A FAIRE :
|
||
Compl<70>ter puis tester le programme principal pour plusieurs valeurs de la profondeur maximale.
|
||
Pmax = 1, 2, 3, 4 ...
|
||
Commentez les r<>sultats obtenus.
|
||
*/
|
||
|
||
/****
|
||
TESTS
|
||
*****/
|
||
list_succ_init(LS) :-
|
||
LS =
|
||
[ [[1,1],[ [x,_,_],[_,_,_],[_,_,_] ]],
|
||
[[1,2],[ [_,x,_],[_,_,_],[_,_,_] ]],
|
||
[[1,3],[ [_,_,x],[_,_,_],[_,_,_] ]],
|
||
[[2,1],[ [_,_,_],[x,_,_],[_,_,_] ]],
|
||
[[2,2],[ [_,_,_],[_,x,_],[_,_,_] ]],
|
||
[[2,3],[ [_,_,_],[_,_,x],[_,_,_] ]],
|
||
[[3,1],[ [_,_,_],[_,_,_],[x,_,_] ]],
|
||
[[3,2],[ [_,_,_],[_,_,_],[_,x,_] ]],
|
||
[[3,3],[ [_,_,_],[_,_,_],[_,_,x] ]] ].
|
||
|
||
|
||
|
||
|
||
test_pred_successeurs :-
|
||
situation_initiale(Situation),
|
||
joueur_initial(J),
|
||
successeurs(J,Situation,
|
||
LS),
|
||
list_succ_init(LS).
|
||
|
||
|