Ajout des fichiers sources prolog
This commit is contained in:
commit
bbf58794f4
8 changed files with 1800 additions and 0 deletions
134
TP1/aetoile.pl
Normal file
134
TP1/aetoile.pl
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
%*******************************************************************************
|
||||||
|
% AETOILE
|
||||||
|
%*******************************************************************************
|
||||||
|
|
||||||
|
/*
|
||||||
|
Rappels sur l'algorithme
|
||||||
|
|
||||||
|
- structures de donnees principales = 2 ensembles : P (etat pendants) et Q (etats clos)
|
||||||
|
- P est dedouble en 2 arbres binaires de recherche equilibres (AVL) : Pf et Pu
|
||||||
|
|
||||||
|
Pf est l'ensemble des etats pendants (pending states), ordonnes selon
|
||||||
|
f croissante (h croissante en cas d'egalite de f). Il permet de trouver
|
||||||
|
rapidement le prochain etat a developper (celui qui a f(U) minimum).
|
||||||
|
|
||||||
|
Pu est le meme ensemble mais ordonne lexicographiquement (selon la donnee de
|
||||||
|
l'etat). Il permet de retrouver facilement n'importe quel etat pendant
|
||||||
|
|
||||||
|
On gere les 2 ensembles de fa<EFBFBD>on synchronisee : chaque fois qu'on modifie
|
||||||
|
(ajout ou retrait d'un etat dans Pf) on fait la meme chose dans Pu.
|
||||||
|
|
||||||
|
Q est l'ensemble des etats deja developpes. Comme Pu, il permet de retrouver
|
||||||
|
facilement un etat par la donnee de sa situation.
|
||||||
|
Q est modelise par un seul arbre binaire de recherche equilibre.
|
||||||
|
|
||||||
|
Predicat principal de l'algorithme :
|
||||||
|
|
||||||
|
aetoile(Pf,Pu,Q)
|
||||||
|
|
||||||
|
- reussit si Pf est vide ou bien contient un etat minimum terminal
|
||||||
|
- sinon on prend un etat minimum U, on genere chaque successeur S et les valeurs g(S) et h(S)
|
||||||
|
et pour chacun
|
||||||
|
si S appartient a Q, on l'oublie
|
||||||
|
si S appartient a Ps (etat deja rencontre), on compare
|
||||||
|
g(S)+h(S) avec la valeur deja calculee pour f(S)
|
||||||
|
si g(S)+h(S) < f(S) on reclasse S dans Pf avec les nouvelles valeurs
|
||||||
|
g et f
|
||||||
|
sinon on ne touche pas a Pf
|
||||||
|
si S est entierement nouveau on l'insere dans Pf et dans Ps
|
||||||
|
- appelle recursivement etoile avec les nouvelles valeurs NewPF, NewPs, NewQs
|
||||||
|
|
||||||
|
*/
|
||||||
|
/*affiche_solution(_, nil).
|
||||||
|
affiche_solution(Q, U) :-
|
||||||
|
U \= nil,
|
||||||
|
belongs([U, _, Pere, A], Q),
|
||||||
|
affiche_solution(Q, Pere),
|
||||||
|
write(A).
|
||||||
|
*/
|
||||||
|
|
||||||
|
affiche_solution(_, _, nil).
|
||||||
|
|
||||||
|
affiche_solution(Q, Pu, U) :-
|
||||||
|
U \= nil,
|
||||||
|
belongs([U, _, Pere, A], Q),
|
||||||
|
affiche_solution(Q, Pu, Pere),
|
||||||
|
write(A),
|
||||||
|
write("->").
|
||||||
|
|
||||||
|
affiche_solution(Q, Pu, U) :-
|
||||||
|
U \= nil,
|
||||||
|
belongs([U, _, Pere, A], Pu),
|
||||||
|
affiche_solution(Q, Pu, Pere),
|
||||||
|
write(A),
|
||||||
|
write("->").
|
||||||
|
|
||||||
|
expand(U, LSuc, G) :-
|
||||||
|
findall([S, [F, H, G2], U, Rule],
|
||||||
|
(rule(Rule, C, U, S),
|
||||||
|
G2 is G+C,
|
||||||
|
heuristique(S, H),
|
||||||
|
F is G2+H),
|
||||||
|
LSuc).
|
||||||
|
test_expand() :-
|
||||||
|
initial_state(U),
|
||||||
|
write(U),
|
||||||
|
expand(U, L, 0),
|
||||||
|
write(L).
|
||||||
|
loop_successors([], Ps,Pf,_,Ps,Pf).
|
||||||
|
|
||||||
|
loop_successors([[S, _, _, _]|LSuc], Ps, Pf, Qs, PsF, PfF) :-
|
||||||
|
belongs([S, _, _, _], Qs),
|
||||||
|
loop_successors(LSuc, Ps, Pf, Qs, PsF, PfF).
|
||||||
|
|
||||||
|
|
||||||
|
loop_successors([[S, [F, H, G], Pere, A]|LSuc], Ps, Pf, Qs, PsF, PfF) :-
|
||||||
|
not(belongs([S, _, _, _], Qs)),
|
||||||
|
belongs([S, [Fu, Hu, Gu], PereU, Au], Ps),
|
||||||
|
F<Fu,
|
||||||
|
suppress([S, [Fu, Hu, Gu], PereU, Au], Ps, Ps2),
|
||||||
|
insert([S, [F, H, G], Pere, A], Ps2, Ps3),
|
||||||
|
suppress([[Fu, Hu, Gu], S], Pf, Pf2),
|
||||||
|
insert([[F, H, G], S], Pf2, Pf3),
|
||||||
|
loop_successors(LSuc, Ps3, Pf3, Qs, PsF, PfF).
|
||||||
|
|
||||||
|
loop_successors([[S, [F, _, _], _, _]|LSuc], Ps, Pf, Qs, PsF, PfF) :-
|
||||||
|
not(belongs([S, _, _, _], Qs)),
|
||||||
|
belongs([S, [Fu, _, _], _, _], Ps),
|
||||||
|
F>Fu,
|
||||||
|
loop_successors(LSuc, Ps, Pf, Qs, PsF, PfF).
|
||||||
|
|
||||||
|
loop_successors([[S, [F, H, G], Pere, A]|LSuc], Ps, Pf, Qs, PsF, PfF) :-
|
||||||
|
not(belongs([S, _, _, _], Qs)),
|
||||||
|
not(belongs([S, _, _, _], Ps)),
|
||||||
|
insert([S, [F, H, G], Pere, A], Ps, Ps2),
|
||||||
|
insert([[F, H, G], S], Pf, Pf2),
|
||||||
|
loop_successors(LSuc, Ps2, Pf2, Qs, PsF, PfF).
|
||||||
|
|
||||||
|
main :-
|
||||||
|
initial_state(S0),
|
||||||
|
heuristique(S0, H0),
|
||||||
|
G0 is 0,
|
||||||
|
F0 is H0+G0,
|
||||||
|
empty(Pf),
|
||||||
|
empty(Pu),
|
||||||
|
empty(Q),
|
||||||
|
insert([[F0,H0,G0], S0], Pf, Pf2),
|
||||||
|
insert([S0, [F0,H0,G0], nil, nil] , Pu, Pu2),
|
||||||
|
aetoile(Pf2, Pu2, Q).
|
||||||
|
|
||||||
|
|
||||||
|
%*******************************************************************************
|
||||||
|
aetoile(nil, nil, _) :-
|
||||||
|
write("PAS DE SOLUTION : l'etat final n est pas atteignable").
|
||||||
|
aetoile(Pf, Ps , Qs) :-
|
||||||
|
suppress_min([_, U], Pf, _),
|
||||||
|
final_state(U),
|
||||||
|
affiche_solution(Qs, Ps, U).
|
||||||
|
aetoile(Pf, Ps, Qs) :-
|
||||||
|
suppress_min([[F, H, G], U], Pf, Pf2),
|
||||||
|
suppress([U, [F, H, G] , UPere , A], Ps, Ps2),
|
||||||
|
expand(U, LSuc, G),
|
||||||
|
loop_successors(LSuc, Ps2, Pf2, Qs, PsF, PfF),
|
||||||
|
insert([U, [F, H, G], UPere, A], Qs, Qs2),
|
||||||
|
aetoile(PfF, PsF, Qs2).
|
367
TP1/avl.pl
Normal file
367
TP1/avl.pl
Normal file
|
@ -0,0 +1,367 @@
|
||||||
|
%***************************
|
||||||
|
% Gestion d'un AVL en Prolog
|
||||||
|
%***************************
|
||||||
|
|
||||||
|
%***************************
|
||||||
|
% INSA TOULOUSE - P.ESQUIROL
|
||||||
|
% mars 2017
|
||||||
|
%***************************
|
||||||
|
|
||||||
|
%*************************
|
||||||
|
% unit tests : OK
|
||||||
|
% integration aetoile : OK
|
||||||
|
%*************************
|
||||||
|
|
||||||
|
% Les AVL sont des arbres BINAIRES DE RECHERCHE H-EQUILIBRES :
|
||||||
|
% La hauteur de l'avl A est définie par :
|
||||||
|
% -1, si A est vide (A=nil)
|
||||||
|
% 1 + max( hauteur(ss_arbre_gauche(A)), hauteur(ss_arbre_droitee(A)) ) sinon
|
||||||
|
|
||||||
|
% Tout noeud de l'arbre est soit :
|
||||||
|
% - une feuille
|
||||||
|
% - un noeud interne tel que la différence de hauteur entre le sous-arbre droit
|
||||||
|
% et le sous-arbre gauche appartient à [-1,0,+1]
|
||||||
|
|
||||||
|
|
||||||
|
%***********************************************
|
||||||
|
% PREDICATS EXPORTES ET COMPLEXITE ALGORITHMIQUE
|
||||||
|
%***********************************************
|
||||||
|
% soit N = nombre de noeuds de l'arbre % UTILITE POUR A*
|
||||||
|
% % ----------------
|
||||||
|
% empty(?Avl) O(1) %<<< initialisation de P et Q
|
||||||
|
% height(+Avl, ?Height) O(1)
|
||||||
|
% put_flat(+Avl) O(N)
|
||||||
|
% put_90(+Avl) O(N)
|
||||||
|
% belongs(+Elem, +Avl) O(log N) %<<< appartenance d'un noeud à Q
|
||||||
|
% subtree(+Elem, +Avl, Ss_Avl) O(log N)
|
||||||
|
% insert(+Elem, +Avant, ?Apres) O(log N) %<<< insertion d'un nouveau noeud dans P ou dans Q
|
||||||
|
% suppress(+Elem,+Avant,?Apres) O(log N) %<<< mise à jour <=> suppression puis insertion
|
||||||
|
% suppress_min(?Min,+Avant,?Apres) O(log N) %<<< supression du noeud minimal
|
||||||
|
% suppress_max(?Max,+Avant,?Apres) O(log N)
|
||||||
|
|
||||||
|
%****************************
|
||||||
|
% Prédicats internes (prives)
|
||||||
|
%****************************
|
||||||
|
|
||||||
|
% left_rotate(+Avant, ?Apres) O(1)
|
||||||
|
% right_rotate(+Avant, ?Apres) O(1)
|
||||||
|
% left_balance(+Avant, ?Apres) O(1)
|
||||||
|
% right_balance(+Avant, ?Apres) O(1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%------------------------------
|
||||||
|
% Constructeur et test AVL vide
|
||||||
|
%------------------------------
|
||||||
|
|
||||||
|
empty(nil).
|
||||||
|
|
||||||
|
%-----------------
|
||||||
|
% Hauteur d'un AVL
|
||||||
|
%-----------------
|
||||||
|
% par convention, un avl vide a une hauteur de -1
|
||||||
|
% sinon la hauteur est enregistree au meme niveau que la racine de l'avl
|
||||||
|
% elle n'est pas calculee recursivement "from scratch"
|
||||||
|
% elle est mise à jour de façon incrémentale, apres chaque insertion ou suppression
|
||||||
|
% d'ou sa complexité en O(1) :-)
|
||||||
|
|
||||||
|
height(nil, -1).
|
||||||
|
height(avl(_G,_R,_D, H), H).
|
||||||
|
|
||||||
|
%-------------------
|
||||||
|
% Affichage d'un AVL
|
||||||
|
%-------------------
|
||||||
|
% dans l'ordre croissant (lexicographique)
|
||||||
|
|
||||||
|
put_flat(nil).
|
||||||
|
put_flat(avl(G,R,D,_H)) :-
|
||||||
|
put_flat(G),
|
||||||
|
nl, write(R),
|
||||||
|
put_flat(D).
|
||||||
|
|
||||||
|
%----------------------------
|
||||||
|
% Affichage (couché) d'un AVL
|
||||||
|
%----------------------------
|
||||||
|
|
||||||
|
put_90(Avl) :-
|
||||||
|
nl, writeln('----------------------------------'),
|
||||||
|
put_90(Avl,"").
|
||||||
|
|
||||||
|
put_90(nil,Str) :-
|
||||||
|
write(Str), write('.').
|
||||||
|
put_90(avl(G,R,D,_H),Str) :-
|
||||||
|
append_strings(Str, " ", Str2),
|
||||||
|
put_90(D,Str2),
|
||||||
|
nl, write(Str), write(R),nl,
|
||||||
|
put_90(G,Str2).
|
||||||
|
|
||||||
|
%-----------------------------------------
|
||||||
|
% Appartenance d'un element donne a un AVL
|
||||||
|
%-----------------------------------------
|
||||||
|
|
||||||
|
belongs(Elem, avl(G,Racine,D,_Hauteur)) :-
|
||||||
|
(Elem = Racine ->
|
||||||
|
true
|
||||||
|
;
|
||||||
|
(Elem @< Racine ->
|
||||||
|
belongs(Elem, G)
|
||||||
|
;
|
||||||
|
belongs(Elem, D) %Racine @< Elem
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
%------------------------------------------------------------
|
||||||
|
% Recherche du sous-arbre qui a comme racine un element donne
|
||||||
|
%------------------------------------------------------------
|
||||||
|
|
||||||
|
subtree(Elem, avl(G,Racine,D,H), A) :-
|
||||||
|
(Elem = Racine ->
|
||||||
|
A = avl(G,Racine,D,H)
|
||||||
|
;
|
||||||
|
(Elem @< Racine ->
|
||||||
|
subtree(Elem,G,A)
|
||||||
|
;
|
||||||
|
subtree(Elem,D,A) %Racine @< Elem
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
%----------------------
|
||||||
|
% Rotations dans un avl
|
||||||
|
%----------------------
|
||||||
|
% Les rotations ci-dessous décrivent uniquement les cas ou la rotation est possible.
|
||||||
|
% Dans les autres cas, ces relations échouent ; plus précisément :
|
||||||
|
% a/ si l'arbre est un avl vide, alors aucune rotation n'est possible ;
|
||||||
|
% b/ si l'arbre est un avl non vide mais si son ss-arbre gauche est un avl vide
|
||||||
|
% alors la rotation droite n'est pas possible ;
|
||||||
|
% c/ si l'arbre est un avl non vide mais si son ss-arbre droite est un avl vide
|
||||||
|
% alors la rotation gauche n'est pas possible.
|
||||||
|
|
||||||
|
right_rotate(avl(G,R,D,_H), A_Apres) :-
|
||||||
|
height(D,HD),
|
||||||
|
G = avl(SG,RG,SD,_HG),
|
||||||
|
height(SD,HSD),
|
||||||
|
H_Inter is 1 + max(HSD, HD),
|
||||||
|
Inter = avl(SD,R,D,H_Inter),
|
||||||
|
height(SG,HSG),
|
||||||
|
H_Apres is 1 + max(HSG,H_Inter),
|
||||||
|
A_Apres = avl(SG,RG,Inter,H_Apres).
|
||||||
|
|
||||||
|
left_rotate(avl(G,R,D,_), A_Apres) :-
|
||||||
|
height(G,HG),
|
||||||
|
D = avl(SG,RD,SD,_),
|
||||||
|
height(SG,HSG),
|
||||||
|
H_Inter is 1 + max(HSG, HG),
|
||||||
|
Inter = avl(G,R,SG,H_Inter),
|
||||||
|
height(SD,HSD),
|
||||||
|
H_Apres is 1 + max(H_Inter,HSD),
|
||||||
|
A_Apres = avl(Inter,RD,SD,H_Apres).
|
||||||
|
|
||||||
|
%---------------------------------
|
||||||
|
% Insertion equilibree dans un avl
|
||||||
|
%---------------------------------
|
||||||
|
% On suppose que l'arbre avant insertion est equilibré (difference de hauteur
|
||||||
|
% entre les ss-arbres gauche et droite de 1 au maximum)
|
||||||
|
% L'insertion doit assurer qu'apres insertion l'arbre est toujours equilibre
|
||||||
|
% sinon les rotations necessaires sont effectuees.
|
||||||
|
|
||||||
|
% On suppose que les noeuds contiennent des informations que l'on peut comparer
|
||||||
|
% a l'aide d'une relation d'ordre lexicographique (la cle c'est l'info elle-meme)
|
||||||
|
% En prolog, c'est la relation '@<'
|
||||||
|
% On peut comparer par exemple des integer, des string, des constantes,
|
||||||
|
% des listes d'entiers, des listes de constantes, etc ... bref, des termes clos
|
||||||
|
% T1 @< T2 est vrai si T1 est lexicographiquement inférieur a T2.
|
||||||
|
|
||||||
|
insert(Elem, nil, avl(nil,Elem,nil,0)).
|
||||||
|
insert(Elem, AVL, NEW_AVL) :-
|
||||||
|
AVL = avl(Gauche,Racine,Droite,_Hauteur),
|
||||||
|
(Elem = Racine ->
|
||||||
|
% l'élément est déjà present, pas d'insertion possible
|
||||||
|
fail
|
||||||
|
;
|
||||||
|
(Elem @< Racine ->
|
||||||
|
% insertion dans le ss-arbre gauche
|
||||||
|
insert(Elem, Gauche, New_Gauche),
|
||||||
|
height(New_Gauche, New_HG),
|
||||||
|
height(Droite, HD),
|
||||||
|
H_Int is 1+max(New_HG, HD),
|
||||||
|
AVL_INT = avl(New_Gauche, Racine, Droite, H_Int),
|
||||||
|
right_balance(AVL_INT, NEW_AVL)
|
||||||
|
;
|
||||||
|
% Elem @> Racine
|
||||||
|
% insertion dans le ss-arbre droite
|
||||||
|
insert(Elem, Droite, New_Droite),
|
||||||
|
height(New_Droite, New_HD),
|
||||||
|
height(Gauche, HG),
|
||||||
|
H_Int is 1+max(New_HD, HG),
|
||||||
|
AVL_INT =avl(Gauche, Racine,New_Droite, H_Int),
|
||||||
|
left_balance(AVL_INT, NEW_AVL)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
%------------------------------------------------
|
||||||
|
% Suppression d'un element quelconque dans un avl
|
||||||
|
%------------------------------------------------
|
||||||
|
% On suppose que l'élément à supprimer appartient bien à l'AVL,
|
||||||
|
% sinon le predicat échoue (en particulier si l'AVL est vide).
|
||||||
|
|
||||||
|
suppress(Elem, AVL, NEW_AVL) :-
|
||||||
|
AVL = avl(Gauche, Racine, Droite, _Hauteur),
|
||||||
|
(Elem = Racine ->
|
||||||
|
% cas de la suppression de la racine de l'avl
|
||||||
|
(Gauche = nil -> % cas simple d'une feuille ou d'un avl sans fils gauche
|
||||||
|
NEW_AVL = Droite
|
||||||
|
;
|
||||||
|
(Droite = nil -> % cas simple d'un avl avec fils gauche mais sans fils droit
|
||||||
|
NEW_AVL = Gauche
|
||||||
|
;
|
||||||
|
% cas d'un avl avec fils gauche ET fils droit
|
||||||
|
%Gauche \= nil
|
||||||
|
%Droite \= nil
|
||||||
|
suppress_max(Max, Gauche, New_Gauche),
|
||||||
|
AVL_INT = avl(New_Gauche,Max,Droite,_),
|
||||||
|
left_balance(AVL_INT, NEW_AVL)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
% cas des suppressions d'un element autre que la racine
|
||||||
|
(Elem @< Racine ->
|
||||||
|
% suppression dans le ss-arbre gauche
|
||||||
|
suppress(Elem, Gauche, New_Gauche),
|
||||||
|
AVL_INT = avl(New_Gauche, Racine, Droite,_),
|
||||||
|
left_balance(AVL_INT, NEW_AVL)
|
||||||
|
;
|
||||||
|
%Racine @< Droite
|
||||||
|
% suppression dans le ss-arbre droite
|
||||||
|
suppress(Elem, Droite, New_Droite),
|
||||||
|
AVL_INT = avl(Gauche, Racine, New_Droite,_),
|
||||||
|
right_balance(AVL_INT, NEW_AVL)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
%-------------------------------------------------------
|
||||||
|
% Suppression du plus petit element dans un avl non vide
|
||||||
|
%-------------------------------------------------------
|
||||||
|
% Si l'avl est vide, le prédicat échoue
|
||||||
|
|
||||||
|
suppress_min(Min, AVL, NEW_AVL) :-
|
||||||
|
AVL = avl(Gauche,Racine,Droite, _Hauteur),
|
||||||
|
(Gauche = nil ->
|
||||||
|
Min = Racine,
|
||||||
|
NEW_AVL = Droite
|
||||||
|
;
|
||||||
|
% Gauche \= nil
|
||||||
|
suppress_min(Min, Gauche, New_Gauche),
|
||||||
|
AVL_INT = avl(New_Gauche, Racine, Droite,_),
|
||||||
|
left_balance(AVL_INT, NEW_AVL)
|
||||||
|
).
|
||||||
|
|
||||||
|
%-------------------------------------------------------
|
||||||
|
% Suppression du plus grand element dans un avl non vide
|
||||||
|
%-------------------------------------------------------
|
||||||
|
% Si l'avl est vide, le prédicat échoue
|
||||||
|
|
||||||
|
suppress_max(Max, AVL, NEW_AVL) :-
|
||||||
|
AVL = avl(Gauche,Racine,Droite, _Hauteur),
|
||||||
|
(Droite = nil ->
|
||||||
|
Max = Racine,
|
||||||
|
NEW_AVL = Gauche
|
||||||
|
;
|
||||||
|
% Droite \= nil
|
||||||
|
suppress_max(Max, Droite, New_Droite),
|
||||||
|
AVL_INT = avl(Gauche, Racine, New_Droite,_),
|
||||||
|
right_balance(AVL_INT, NEW_AVL)
|
||||||
|
).
|
||||||
|
|
||||||
|
%----------------------------------------
|
||||||
|
% Re-equilibrages d'un avl vers la gauche
|
||||||
|
%----------------------------------------
|
||||||
|
% - soit apres insertion d'un element dans le sous-arbre droite
|
||||||
|
% - soit apres suppression d'un élément dans le sous-arbre gauche
|
||||||
|
%----------------------------------------------------------------
|
||||||
|
|
||||||
|
left_balance(Avl, New_Avl) :-
|
||||||
|
Avl = avl(Gauche, Racine, Droite, _Hauteur),
|
||||||
|
height(Gauche, HG),
|
||||||
|
height(Droite, HD),
|
||||||
|
(HG is HD-2 ->
|
||||||
|
% le sous-arbre droite est trop haut
|
||||||
|
Droite = avl(G_Droite, _R_Droite, D_Droite, _HD),
|
||||||
|
height(G_Droite, HGD),
|
||||||
|
height(D_Droite, HDD),
|
||||||
|
(HDD > HGD ->
|
||||||
|
% une simple rotation gauche suffit
|
||||||
|
left_rotate(Avl, New_Avl)
|
||||||
|
;
|
||||||
|
% il faut faire une rotation droite_gauche
|
||||||
|
right_rotate(Droite, New_Droite),
|
||||||
|
height(New_Droite, New_HD),
|
||||||
|
H_Int is 1+ max(HG, New_HD),
|
||||||
|
Avl_Int = avl(Gauche, Racine, New_Droite, H_Int),
|
||||||
|
left_rotate(Avl_Int, New_Avl)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
% la suppression n'a pas desequilibre l'avl
|
||||||
|
New_Hauteur is 1+max(HG,HD),
|
||||||
|
New_Avl = avl(Gauche, Racine, Droite, New_Hauteur)
|
||||||
|
).
|
||||||
|
|
||||||
|
%----------------------------------------
|
||||||
|
% Re-equilibrages d'un avl vers la droite
|
||||||
|
%----------------------------------------
|
||||||
|
% - soit apres insertion d'un element dans le sous-arbre gauche
|
||||||
|
% - soit apres suppression d'un élément dans le sous-arbre droite
|
||||||
|
%----------------------------------------------------------------
|
||||||
|
|
||||||
|
right_balance(Avl, New_Avl) :-
|
||||||
|
Avl = avl(Gauche, Racine, Droite, _Hauteur),
|
||||||
|
height(Gauche, HG),
|
||||||
|
height(Droite, HD),
|
||||||
|
(HD is HG-2 ->
|
||||||
|
% le sous-arbre gauche est trop haut
|
||||||
|
Gauche = avl(G_Gauche, _R_Gauche, D_Gauche, _HG),
|
||||||
|
height(G_Gauche, HGG),
|
||||||
|
height(D_Gauche, HDG),
|
||||||
|
(HGG > HDG ->
|
||||||
|
% une simple rotation droite suffit
|
||||||
|
right_rotate(Avl, New_Avl)
|
||||||
|
;
|
||||||
|
% il faut faire une rotation gauche_droite
|
||||||
|
left_rotate(Gauche, New_Gauche),
|
||||||
|
height(New_Gauche, New_HG),
|
||||||
|
H_Int is 1+ max(New_HG, HD),
|
||||||
|
Avl_Int = avl(New_Gauche, Racine, Droite, H_Int),
|
||||||
|
right_rotate(Avl_Int, New_Avl)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
% la suppression n'a pas desequilibre l'avl
|
||||||
|
New_Hauteur is 1+max(HG,HD),
|
||||||
|
New_Avl = avl(Gauche, Racine, Droite, New_Hauteur)
|
||||||
|
).
|
||||||
|
|
||||||
|
%-----------------------------------------
|
||||||
|
% Arbres utilises pour les tests unitaires
|
||||||
|
%-----------------------------------------
|
||||||
|
avl_test(1, nil).
|
||||||
|
avl_test(2, avl(nil, 1, nil, 0)).
|
||||||
|
avl_test(3, avl(nil, 1, avl(nil,2,nil,0), 1)).
|
||||||
|
avl_test(4, avl(avl(nil,1,nil,0),2, nil, 1)).
|
||||||
|
avl_test(5, avl(avl(nil,1,nil,0), 2, avl(nil,3,nil,0),1) ).
|
||||||
|
avl_test(6, avl(avl(nil,5,nil,0), 6, avl(nil,7,nil,0),1) ).
|
||||||
|
avl_test(7, avl(G,4,D,2)) :-
|
||||||
|
avl_test(5,G),
|
||||||
|
avl_test(6,D).
|
||||||
|
avl_test(8, avl(G,5,D,2)) :-
|
||||||
|
D = avl(nil,6,nil,0),
|
||||||
|
avl_test(3,G).
|
||||||
|
avl_test(9, avl(G,3,D,2)) :-
|
||||||
|
G = avl(nil,1,nil,0),
|
||||||
|
avl_test(4,D).
|
||||||
|
|
||||||
|
/* Test uniquement valable avec ECLiPSe
|
||||||
|
|
||||||
|
avl_test(10, Final) :-
|
||||||
|
empty(Init),
|
||||||
|
(for(I,1,20), fromto(Init,In,Out,Final) do
|
||||||
|
insert(I,In,Out)
|
||||||
|
).
|
||||||
|
*/
|
228
TP1/taquin.pl
Normal file
228
TP1/taquin.pl
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
/* Fichier du probleme.
|
||||||
|
|
||||||
|
Doit contenir au moins 4 predicats qui seront utilises par A*
|
||||||
|
|
||||||
|
etat_initial(I) % definit l'etat initial
|
||||||
|
|
||||||
|
etat_final(F) % definit l'etat final
|
||||||
|
|
||||||
|
rule(Rule_Name, Rule_Cost, Before_State, After_State) % règles applicables
|
||||||
|
|
||||||
|
heuristique(Current_State, Hval) % calcul de l'heuristique
|
||||||
|
|
||||||
|
|
||||||
|
Les autres prédicats sont spécifiques au Taquin.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
%:- lib(listut). % Laisser cette directive en commentaire si vous utilisez Swi-Prolog
|
||||||
|
|
||||||
|
% Sinon décommentez la ligne si vous utilisez ECLiPSe Prolog :
|
||||||
|
% -> permet de disposer du predicat nth1(N, List, E)
|
||||||
|
% -> permet de disposer du predicat sumlist(List, S)
|
||||||
|
% (qui sont predefinis en Swi-Prolog)
|
||||||
|
|
||||||
|
|
||||||
|
%***************************
|
||||||
|
%DESCRIPTION DU JEU DU TAKIN
|
||||||
|
%***************************
|
||||||
|
|
||||||
|
%********************
|
||||||
|
% ETAT INITIAL DU JEU
|
||||||
|
%********************
|
||||||
|
% format : initial_state(+State) ou State est une matrice (liste de listes)
|
||||||
|
|
||||||
|
|
||||||
|
initial_state([ [b, h, c], % C'EST L'EXEMPLE PRIS EN COURS
|
||||||
|
[a, f, d], %
|
||||||
|
[g,vide,e] ]). % h1=4, h2=5, f*=5
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
% AUTRES EXEMPLES POUR LES TESTS DE A*
|
||||||
|
|
||||||
|
/*
|
||||||
|
initial_state([ [ a, b, c],
|
||||||
|
[ g, h, d],
|
||||||
|
[vide,f, e] ]). % h2=2, f*=2
|
||||||
|
|
||||||
|
initial_state([ [b, c, d],
|
||||||
|
[a,vide,g],
|
||||||
|
[f, h, e] ]). % h2=10 f*=10
|
||||||
|
|
||||||
|
initial_state([ [f, g, a],
|
||||||
|
[h,vide,b],
|
||||||
|
[d, c, e] ]). % h2=16, f*=20
|
||||||
|
|
||||||
|
initial_state([ [e, f, g],
|
||||||
|
[d,vide,h],
|
||||||
|
[c, b, a] ]). % h2=24, f*=30
|
||||||
|
|
||||||
|
initial_state([ [a, b, c],
|
||||||
|
[g,vide,d],
|
||||||
|
[h, f, e]]). % etat non connexe avec l'etat final (PAS DE SOLUTION)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
%******************
|
||||||
|
% ETAT FINAL DU JEU
|
||||||
|
%******************
|
||||||
|
% format : final_state(+State) ou State est une matrice (liste de listes)
|
||||||
|
|
||||||
|
final_state([[a, b, c],
|
||||||
|
[h,vide, d],
|
||||||
|
[g, f, e]]).
|
||||||
|
|
||||||
|
|
||||||
|
%********************
|
||||||
|
% AFFICHAGE D'UN ETAT
|
||||||
|
%********************
|
||||||
|
% format : write_state(?State) ou State est une liste de lignes a afficher
|
||||||
|
|
||||||
|
write_state([]).
|
||||||
|
write_state([Line|Rest]) :-
|
||||||
|
writeln(Line),
|
||||||
|
write_state(Rest).
|
||||||
|
|
||||||
|
|
||||||
|
%**********************************************
|
||||||
|
% REGLES DE DEPLACEMENT (up, down, left, right)
|
||||||
|
%**********************************************
|
||||||
|
% format : rule(+Rule_Name, ?Rule_Cost, +Current_State, ?Next_State)
|
||||||
|
|
||||||
|
rule(up, 1, S1, S2) :-
|
||||||
|
vertical_permutation(_X,vide,S1,S2).
|
||||||
|
|
||||||
|
rule(down, 1, S1, S2) :-
|
||||||
|
vertical_permutation(vide,_X,S1,S2).
|
||||||
|
|
||||||
|
rule(left, 1, S1, S2) :-
|
||||||
|
horizontal_permutation(_X,vide,S1,S2).
|
||||||
|
|
||||||
|
rule(right,1, S1, S2) :-
|
||||||
|
horizontal_permutation(vide,_X,S1,S2).
|
||||||
|
|
||||||
|
%***********************
|
||||||
|
% Deplacement horizontal
|
||||||
|
%***********************
|
||||||
|
% format : horizontal_permutation(?Piece1,?Piece2,+Current_State, ?Next_State)
|
||||||
|
|
||||||
|
horizontal_permutation(X,Y,S1,S2) :-
|
||||||
|
append(Above,[Line1|Rest], S1),
|
||||||
|
exchange(X,Y,Line1,Line2),
|
||||||
|
append(Above,[Line2|Rest], S2).
|
||||||
|
|
||||||
|
%***********************************************
|
||||||
|
% Echange de 2 objets consecutifs dans une liste
|
||||||
|
%***********************************************
|
||||||
|
|
||||||
|
exchange(X,Y,[X,Y|List], [Y,X|List]).
|
||||||
|
exchange(X,Y,[Z|List1], [Z|List2] ):-
|
||||||
|
exchange(X,Y,List1,List2).
|
||||||
|
|
||||||
|
%*********************
|
||||||
|
% Deplacement vertical
|
||||||
|
%*********************
|
||||||
|
|
||||||
|
vertical_permutation(X,Y,S1,S2) :-
|
||||||
|
append(Above, [Line1,Line2|Below], S1), % decompose S1
|
||||||
|
delete(N,X,Line1,Rest1), % enleve X en position N a Line1, donne Rest1
|
||||||
|
delete(N,Y,Line2,Rest2), % enleve Y en position N a Line2, donne Rest2
|
||||||
|
delete(N,Y,Line3,Rest1), % insere Y en position N dans Rest1 donne Line3
|
||||||
|
delete(N,X,Line4,Rest2), % insere X en position N dans Rest2 donne Line4
|
||||||
|
append(Above, [Line3,Line4|Below], S2). % recompose S2
|
||||||
|
|
||||||
|
%***********************************************************************
|
||||||
|
% Retrait d'une occurrence X en position N dans une liste L (resultat R)
|
||||||
|
%***********************************************************************
|
||||||
|
% use case 1 : delete(?N,?X,+L,?R)
|
||||||
|
% use case 2 : delete(?N,?X,?L,+R)
|
||||||
|
|
||||||
|
delete(1,X,[X|L], L).
|
||||||
|
delete(N,X,[Y|L], [Y|R]) :-
|
||||||
|
delete(N1,X,L,R),
|
||||||
|
N is N1 + 1.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
%*******************
|
||||||
|
% PARTIE A COMPLETER
|
||||||
|
%*******************
|
||||||
|
|
||||||
|
%*******************************************************************
|
||||||
|
% Coordonnees X(colonne),Y(Ligne) d'une piece P dans une situation U
|
||||||
|
%*******************************************************************
|
||||||
|
% format : coordonnees(?Coord, +Matrice, ?Element)
|
||||||
|
% Définit la relation entre des coordonnees [Ligne, Colonne] et un element de la matrice
|
||||||
|
/*
|
||||||
|
Exemples
|
||||||
|
|
||||||
|
?- coordonnees(Coord, [[a,b,c],[d,e,f]], e). % quelles sont les coordonnees de e ?
|
||||||
|
Coord = [2,2]
|
||||||
|
yes
|
||||||
|
|
||||||
|
?- coordonnees([2,3], [[a,b,c],[d,e,f]], P). % qui a les coordonnees [2,3] ?
|
||||||
|
P=f
|
||||||
|
yes
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
coordonnees(Elt, Mat, [L,C]) :-
|
||||||
|
nth1(L, Mat, Ligne),
|
||||||
|
nth1(C, Ligne, Elt).
|
||||||
|
|
||||||
|
|
||||||
|
%*************
|
||||||
|
% HEURISTIQUES
|
||||||
|
%*************
|
||||||
|
|
||||||
|
heuristique(U,H) :-
|
||||||
|
%heuristique1(U, H). % au debut on utilise l'heuristique 1
|
||||||
|
heuristique2(U, H). % ensuite utilisez plutot l'heuristique 2
|
||||||
|
|
||||||
|
|
||||||
|
%****************
|
||||||
|
%HEURISTIQUE no 1
|
||||||
|
%****************
|
||||||
|
% Nombre de pieces mal placees dans l'etat courant U
|
||||||
|
% par rapport a l'etat final F
|
||||||
|
|
||||||
|
|
||||||
|
% Suggestions : définir d'abord le prédicat coordonnees(Piece,Etat,Lig,Col) qui associe à une pièce présente dans Etat
|
||||||
|
% ses coordonnees (Lig= numero de ligne, Col= numero de Colonne)
|
||||||
|
|
||||||
|
% Definir ensuite le predicat malplace(P,U,F) qui est vrai si les coordonnes de P dans U et dans F sont differentes.
|
||||||
|
% On peut également comparer les pieces qui se trouvent aux mêmes coordonnees dans U et dans H et voir s'il sagit de la
|
||||||
|
% même piece.
|
||||||
|
|
||||||
|
% Definir enfin l'heuristique qui détermine toutes les pièces mal placées (voir prédicat findall)
|
||||||
|
% et les compte (voir prédicat length)
|
||||||
|
mal_place(Lettre, S, F) :-
|
||||||
|
coordonnees(Lettre, S, [L,C]),
|
||||||
|
coordonnees(Lettre, F, [L2,C2]),
|
||||||
|
[L,C]\=[L2,C2],
|
||||||
|
Lettre\=vide.
|
||||||
|
|
||||||
|
heuristique1(U, H) :-
|
||||||
|
final_state(F),
|
||||||
|
findall(X, mal_place(X, U, F), L),
|
||||||
|
length(L, H).
|
||||||
|
|
||||||
|
|
||||||
|
%****************
|
||||||
|
%HEURISTIQUE no 2
|
||||||
|
%****************
|
||||||
|
|
||||||
|
% Somme des distances de Manhattan à parcourir par chaque piece
|
||||||
|
% entre sa position courante et sa positon dans l'etat final
|
||||||
|
formule_manhattan(Lettre, U, F, D) :-
|
||||||
|
coordonnees(Lettre, U, [L, C]),
|
||||||
|
coordonnees(Lettre, F, [L2, C2]),
|
||||||
|
X is abs(L-L2),
|
||||||
|
Y is abs(C-C2),
|
||||||
|
D is (X+Y).
|
||||||
|
|
||||||
|
heuristique2(U, H) :-
|
||||||
|
final_state(F),
|
||||||
|
findall(D, (formule_manhattan(X, U, F, D), X\=vide), List),
|
||||||
|
sumlist(List, H).
|
179
TP2/Elise_Non_Optimise/negamax.pl
Normal file
179
TP2/Elise_Non_Optimise/negamax.pl
Normal file
|
@ -0,0 +1,179 @@
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
Ce programme met en oeuvre l'algorithme Minmax (avec convention
|
||||||
|
negamax) et l'illustre sur le jeu du TicTacToe (morpion 3x3)
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************
|
||||||
|
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
|
||||||
|
.....................................
|
||||||
|
*/
|
||||||
|
%Cas 1 : on est à la profondeur max
|
||||||
|
negamax(J, Etat, Pmax, Pmax, [nil, Val]) :-
|
||||||
|
heuristique(J, Etat, Val).
|
||||||
|
|
||||||
|
%Cas 2 : J ne peut pas jouer (tableau complet)
|
||||||
|
negamax(J, Etat, P, Pmax, [nil, Val]) :-
|
||||||
|
P \= Pmax,
|
||||||
|
situation_terminale(J, Etat),
|
||||||
|
heuristique(J, Etat, Val).
|
||||||
|
|
||||||
|
%Cas 3 : Profondeur max pas atteinte J peut encore jouer
|
||||||
|
negamax(J, Etat, P, Pmax, [Coup, Val]) :-
|
||||||
|
P\=Pmax,
|
||||||
|
not(situation_terminale(J, Etat)),
|
||||||
|
successeurs(J, Etat, Succ),
|
||||||
|
loop_negamax(J, P, Pmax, Succ, ListeCouples),
|
||||||
|
meilleur(ListeCouples, [Coup, V1]),
|
||||||
|
Val 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),
|
||||||
|
adversaire(J,A),
|
||||||
|
Pnew is P+1,
|
||||||
|
negamax(A,Suiv,Pnew,Pmax, [_,Vsuiv]).
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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([A], A).
|
||||||
|
meilleur([[CX, VX]|T],MeilleurCouple) :-
|
||||||
|
T \= [],
|
||||||
|
meilleur(T, [_, VY]),
|
||||||
|
VX=<VY,
|
||||||
|
MeilleurCouple = [CX, VX].
|
||||||
|
|
||||||
|
|
||||||
|
meilleur([[_, VX]|T], MeilleurCouple) :-
|
||||||
|
T \= [],
|
||||||
|
meilleur(T, [CY, VY]),
|
||||||
|
VX>VY,
|
||||||
|
MeilleurCouple = [CY, VY].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************
|
||||||
|
PROGRAMME PRINCIPAL
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
main(B,V, Pmax) :-
|
||||||
|
situation_initiale(S),
|
||||||
|
joueur_initial(J),
|
||||||
|
negamax(J, S, 1, Pmax, [B, V]).
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
A FAIRE :
|
||||||
|
Compléter puis tester le programme principal pour plusieurs valeurs de la profondeur maximale.
|
||||||
|
Pmax = 1, 2, 3, 4 ...
|
||||||
|
Commentez les résultats obtenus.
|
||||||
|
*/
|
||||||
|
|
||||||
|
cpu_time(Goal, Elapsed_Time) :-
|
||||||
|
statistics(process_cputime,Start),
|
||||||
|
call(Goal),
|
||||||
|
statistics(process_cputime,Finish),
|
||||||
|
Elapsed_Time is Finish-Start.
|
221
TP2/Elise_Non_Optimise/tictactoe.pl
Normal file
221
TP2/Elise_Non_Optimise/tictactoe.pl
Normal file
|
@ -0,0 +1,221 @@
|
||||||
|
/*********************************
|
||||||
|
DESCRIPTION DU JEU DU TIC-TAC-TOE
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Une situation est decrite par une matrice 3x3.
|
||||||
|
Chaque case est soit un emplacement libre (Variable LIBRE), soit contient le symbole d'un des 2 joueurs (o ou x)
|
||||||
|
|
||||||
|
Contrairement a la convention du tp precedent, pour modeliser une case libre
|
||||||
|
dans une matrice on n'utilise pas une constante speciale (ex : nil, 'vide', 'libre','inoccupee' ...);
|
||||||
|
On utilise plutôt un identificateur de variable, qui n'est pas unifiee (ex : X, A, ... ou _) .
|
||||||
|
La situation initiale est une "matrice" 3x3 (liste de 3 listes de 3 termes chacune)
|
||||||
|
où chaque terme est une variable libre.
|
||||||
|
Chaque coup d'un des 2 joureurs consiste a donner une valeur (symbole x ou o) a une case libre de la grille
|
||||||
|
et non a deplacer des symboles deja presents sur la grille.
|
||||||
|
|
||||||
|
Pour placer un symbole dans une grille S1, il suffit d'unifier une des variables encore libres de la matrice S1,
|
||||||
|
soit en ecrivant directement Case=o ou Case=x, ou bien en accedant a cette case avec les predicats member, nth1, ...
|
||||||
|
La grille S1 a change d'etat, mais on n'a pas besoin de 2 arguments representant la grille avant et apres le coup,
|
||||||
|
un seul suffit.
|
||||||
|
Ainsi si on joue un coup en S, S perd une variable libre, mais peut continuer a s'appeler S (on n'a pas besoin de la designer
|
||||||
|
par un nouvel identificateur).
|
||||||
|
*/
|
||||||
|
|
||||||
|
situation_initiale([ [_,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_] ]).
|
||||||
|
|
||||||
|
% Convention (arbitraire) : c'est x qui commence
|
||||||
|
|
||||||
|
joueur_initial(x).
|
||||||
|
|
||||||
|
|
||||||
|
% Definition de la relation adversaire/2
|
||||||
|
|
||||||
|
adversaire(x,o).
|
||||||
|
adversaire(o,x).
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************
|
||||||
|
DEFINIR ICI a l'aide du predicat ground/1 comment
|
||||||
|
reconnaitre une situation terminale dans laquelle il
|
||||||
|
n'y a aucun emplacement libre : aucun joueur ne peut
|
||||||
|
continuer a jouer (quel qu'il soit).
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
situation_terminale(_Joueur, Situation) :-
|
||||||
|
ground(Situation).
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
DEFINITIONS D'UN ALIGNEMENT
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
alignement(L, Matrix) :- ligne( L,Matrix).
|
||||||
|
alignement(C, Matrix) :- colonne( C,Matrix).
|
||||||
|
alignement(D, Matrix) :- diagonale(D,Matrix).
|
||||||
|
|
||||||
|
/********************************************
|
||||||
|
DEFINIR ICI chaque type d'alignement maximal
|
||||||
|
existant dans une matrice carree NxN.
|
||||||
|
********************************************/
|
||||||
|
|
||||||
|
ligne(L, M) :-
|
||||||
|
nth1(_ ,M, L).
|
||||||
|
|
||||||
|
colonne(C,M) :-
|
||||||
|
colonne2(_, M, C).
|
||||||
|
|
||||||
|
colonne2(_N, [],[]).
|
||||||
|
colonne2(N, [L|R], [X|C]):-
|
||||||
|
nth1(N, L, X),
|
||||||
|
colonne2(N, R, C).
|
||||||
|
/* Definition de la relation liant une diagonale D a la matrice M dans laquelle elle se trouve.
|
||||||
|
il y en a 2 sortes de diagonales dans une matrice carree(https://fr.wikipedia.org/wiki/Diagonale) :
|
||||||
|
- la premiere diagonale (principale) : (A I)
|
||||||
|
- la seconde diagonale : (Z R)
|
||||||
|
A . . . . . . . Z
|
||||||
|
. \ . . . . . / .
|
||||||
|
. . \ . . . / . .
|
||||||
|
. . . \ . / . . .
|
||||||
|
. . . . X . . .
|
||||||
|
. . . / . \ . . .
|
||||||
|
. . / . . . \ . .
|
||||||
|
. / . . . . . \ .
|
||||||
|
R . . . . . . . I
|
||||||
|
*/
|
||||||
|
|
||||||
|
diagonale(D, M) :-
|
||||||
|
premiere_diag(1,D,M).
|
||||||
|
|
||||||
|
% deuxieme definition A COMPLETER
|
||||||
|
|
||||||
|
diagonale(D, M) :-
|
||||||
|
length(M, N),
|
||||||
|
seconde_diag(N,D,M).
|
||||||
|
|
||||||
|
|
||||||
|
premiere_diag(_,[],[]).
|
||||||
|
premiere_diag(K,[E|D],[Ligne|M]) :-
|
||||||
|
nth1(K,Ligne,E),
|
||||||
|
K1 is K+1,
|
||||||
|
premiere_diag(K1,D,M).
|
||||||
|
|
||||||
|
seconde_diag(_,[],[]).
|
||||||
|
seconde_diag(K,[E|D],[Ligne|M]) :-
|
||||||
|
nth1(K,Ligne,E),
|
||||||
|
K1 is K-1,
|
||||||
|
seconde_diag(K1,D,M).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
DEFINITION D'UN ALIGNEMENT
|
||||||
|
POSSIBLE POUR UN JOUEUR DONNE
|
||||||
|
*****************************/
|
||||||
|
|
||||||
|
possible([X|L], J) :- unifiable(X,J), possible(L,J).
|
||||||
|
possible( [], _).
|
||||||
|
|
||||||
|
/* Attention
|
||||||
|
il faut juste verifier le caractere unifiable
|
||||||
|
de chaque emplacement de la liste, mais il ne
|
||||||
|
faut pas realiser l'unification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
% A FAIRE
|
||||||
|
unifiable(X,_J) :-
|
||||||
|
var(X).
|
||||||
|
|
||||||
|
unifiable(X,J) :-
|
||||||
|
ground(X),
|
||||||
|
X=J.
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
DEFINITION D'UN ALIGNEMENT GAGNANT
|
||||||
|
OU PERDANT POUR UN JOUEUR DONNE J
|
||||||
|
**********************************/
|
||||||
|
/*
|
||||||
|
Un alignement gagnant pour J est un alignement
|
||||||
|
possible pour J qui n'a aucun element encore libre.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remarque : le predicat ground(X) permet de verifier qu'un terme
|
||||||
|
prolog quelconque ne contient aucune partie variable (libre).
|
||||||
|
exemples :
|
||||||
|
?- ground(Var).
|
||||||
|
no
|
||||||
|
?- ground([1,2]).
|
||||||
|
yes
|
||||||
|
?- ground(toto(nil)).
|
||||||
|
yes
|
||||||
|
?- ground( [1, toto(nil), foo(a,B,c)] ).
|
||||||
|
no
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Un alignement perdant pour J est un alignement gagnant pour son adversaire. */
|
||||||
|
|
||||||
|
% A FAIRE
|
||||||
|
|
||||||
|
alignement_gagnant(Ali, J) :-
|
||||||
|
ground(Ali),
|
||||||
|
possible(Ali, J).
|
||||||
|
|
||||||
|
alignement_perdant(Ali, J) :-
|
||||||
|
adversaire(J, A),
|
||||||
|
alignement_gagnant(Ali, A).
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************
|
||||||
|
DEFINITION D'UN ETAT SUCCESSEUR
|
||||||
|
****************************** */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Il faut definir quelle operation subit la matrice
|
||||||
|
M representant l'Etat courant
|
||||||
|
lorsqu'un joueur J joue en coordonnees [L,C]
|
||||||
|
*/
|
||||||
|
|
||||||
|
% A FAIRE
|
||||||
|
successeur(J, Etat,[L,C]) :-
|
||||||
|
nth1(L, Etat, L2),
|
||||||
|
nth1(C, L2, J).
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
EVALUATION HEURISTIQUE D'UNE SITUATION
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
1/ l'heuristique est +infini si la situation J est gagnante pour J
|
||||||
|
2/ l'heuristique est -infini si la situation J est perdante pour J
|
||||||
|
3/ sinon, on fait la difference entre :
|
||||||
|
le nombre d'alignements possibles pour J
|
||||||
|
moins
|
||||||
|
le nombre d'alignements possibles pour l'adversaire de J
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :- % cas 1
|
||||||
|
H = 10000, % grand nombre approximant +infini
|
||||||
|
alignement(Alig,Situation),
|
||||||
|
alignement_gagnant(Alig,J),!.
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :- % cas 2
|
||||||
|
H = -10000, % grand nombre approximant -infini
|
||||||
|
alignement(Alig,Situation),
|
||||||
|
alignement_perdant(Alig,J),!.
|
||||||
|
|
||||||
|
|
||||||
|
% on ne vient ici que si les cut precedents n'ont pas fonctionne,
|
||||||
|
% c-a-d si Situation n'est ni perdante ni gagnante.
|
||||||
|
|
||||||
|
% A FAIRE cas 3
|
||||||
|
heuristique(J,Situation,H) :-
|
||||||
|
adversaire(J, A),
|
||||||
|
findall(D, (alignement(D, Situation), possible(D, J)), List),
|
||||||
|
findall(D, (alignement(D, Situation), possible(D, A)), List2),
|
||||||
|
length(List, NJ),
|
||||||
|
length(List2, NA),
|
||||||
|
H is NJ-NA.
|
||||||
|
|
176
TP2/Paul_Optimise_et_Jeu/morpion.pl
Normal file
176
TP2/Paul_Optimise_et_Jeu/morpion.pl
Normal file
|
@ -0,0 +1,176 @@
|
||||||
|
:- [tictactoe].
|
||||||
|
:- [negamax].
|
||||||
|
|
||||||
|
aff_charac(A) :-
|
||||||
|
var(A),
|
||||||
|
write(" ").
|
||||||
|
|
||||||
|
aff_charac(A) :-
|
||||||
|
ground(A),
|
||||||
|
write(A).
|
||||||
|
|
||||||
|
aff_lign([A, B, C]) :-
|
||||||
|
write("| "),
|
||||||
|
aff_charac(A),
|
||||||
|
write(" | "),
|
||||||
|
aff_charac(B),
|
||||||
|
write(" | "),
|
||||||
|
aff_charac(C),
|
||||||
|
writeln(" |").
|
||||||
|
|
||||||
|
|
||||||
|
aff_situation([L1, L2, L3]) :-
|
||||||
|
writeln(""),
|
||||||
|
writeln(" _____ _____ _____ "),
|
||||||
|
writeln("| | | |"),
|
||||||
|
aff_lign(L1),
|
||||||
|
writeln("|_____|_____|_____|"),
|
||||||
|
writeln("| | | |"),
|
||||||
|
aff_lign(L2),
|
||||||
|
writeln("|_____|_____|_____|"),
|
||||||
|
writeln("| | | |"),
|
||||||
|
aff_lign(L3),
|
||||||
|
writeln("|_____|_____|_____|"),
|
||||||
|
writeln("").
|
||||||
|
|
||||||
|
read_coord(L, C) :-
|
||||||
|
writeln("Veuillez saisir la ligne et la colone ou vous voulez jouer."),
|
||||||
|
read(L),
|
||||||
|
read(C).
|
||||||
|
|
||||||
|
coord_ok(Grille, L, C) :-
|
||||||
|
number(L),
|
||||||
|
number(C),
|
||||||
|
L < 4,
|
||||||
|
C < 4,
|
||||||
|
L > 0,
|
||||||
|
C > 0,
|
||||||
|
nth1(L, Grille, Ligne),
|
||||||
|
nth1(C, Ligne, Valeur),
|
||||||
|
var(Valeur).
|
||||||
|
|
||||||
|
jouer(Grille, L, C, J) :-
|
||||||
|
nth1(L, Grille, Ligne),
|
||||||
|
nth1(C, Ligne, J).
|
||||||
|
|
||||||
|
|
||||||
|
play_local(Grille, J, Prof) :-
|
||||||
|
aff_situation(Grille),
|
||||||
|
read_coord(L, C),
|
||||||
|
(coord_ok(Grille, L, C) ->
|
||||||
|
(jouer(Grille, L, C, J),
|
||||||
|
adversaire(J, A),
|
||||||
|
play_ordi(Grille, A, Prof))
|
||||||
|
;
|
||||||
|
(writeln("Impossible !"),
|
||||||
|
play_local(Grille, J, Prof))
|
||||||
|
).
|
||||||
|
|
||||||
|
play_ordi(Grille, J, Prof) :-
|
||||||
|
negamax(J, Grille, 0, Prof, [Coup, V]),
|
||||||
|
(V = 10000 ->
|
||||||
|
(Coup = [L, C],
|
||||||
|
jouer(Grille, L, C, J),
|
||||||
|
write_loose(Grille))
|
||||||
|
;
|
||||||
|
(V = -10000 ->
|
||||||
|
write_win(Grille)
|
||||||
|
;
|
||||||
|
(Coup = nill ->
|
||||||
|
write_equality(Grille)
|
||||||
|
;
|
||||||
|
(Coup = [L, C],
|
||||||
|
jouer(Grille, L, C, J),
|
||||||
|
adversaire(J, A),
|
||||||
|
play_local(Grille, A, Prof))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
play(Prof) :-
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
situation_initiale(I),
|
||||||
|
joueur_initial(J),
|
||||||
|
play_local(I, J, Prof).
|
||||||
|
|
||||||
|
write_loose(Grille) :-
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
aff_situation(Grille),
|
||||||
|
writeln(""),
|
||||||
|
writeln("YOU LOST !!!").
|
||||||
|
|
||||||
|
write_win(Grille) :-
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
aff_situation(Grille),
|
||||||
|
writeln(""),
|
||||||
|
writeln("YOU WON !!!").
|
||||||
|
|
||||||
|
write_equality(Grille) :-
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
writeln(""),
|
||||||
|
aff_situation(Grille),
|
||||||
|
writeln(""),
|
||||||
|
writeln("EQUALITY").
|
181
TP2/Paul_Optimise_et_Jeu/negamax.pl
Normal file
181
TP2/Paul_Optimise_et_Jeu/negamax.pl
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
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, P, P, [nill, H]) :-
|
||||||
|
heuristique(J,Etat,H), !.
|
||||||
|
|
||||||
|
negamax(J, Etat, _, _, [nill, H]) :-
|
||||||
|
situation_terminale(J, Etat),
|
||||||
|
heuristique(J,Etat,H), !.
|
||||||
|
|
||||||
|
negamax(J, Etat, _, _, [nill, H]) :-
|
||||||
|
heuristique(J,Etat,H),
|
||||||
|
H = 10000, !.
|
||||||
|
|
||||||
|
negamax(J, Etat, _, _, [nill, H]) :-
|
||||||
|
heuristique(J,Etat,H),
|
||||||
|
H = -10000, !.
|
||||||
|
|
||||||
|
negamax(J, Etat, P, Pmax, [Coup, V2]) :-
|
||||||
|
P < Pmax,
|
||||||
|
successeurs(J, Etat, Succ),
|
||||||
|
loop_negamax(J, P, Pmax, Succ, L),
|
||||||
|
meilleur(L, [Coup, V1]),
|
||||||
|
V2 is 0 - 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),
|
||||||
|
adversaire(J,A),
|
||||||
|
Pnew is P+1,
|
||||||
|
negamax(A,Suiv,Pnew,Pmax, [_,Vsuiv]).
|
||||||
|
|
||||||
|
/*
|
||||||
|
|
||||||
|
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([[_, V] | Rest], [C1, V1]) :-
|
||||||
|
Rest \= [],
|
||||||
|
meilleur(Rest, [C1, V1]),
|
||||||
|
V >= V1,
|
||||||
|
!.
|
||||||
|
meilleur([[C, V] | Rest], [C, V]) :-
|
||||||
|
Rest \= [].
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/******************
|
||||||
|
PROGRAMME PRINCIPAL
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
main(B,V, Pmax) :-
|
||||||
|
situation_initiale(I),
|
||||||
|
/*I = [[_,o,_],
|
||||||
|
[x,x,_],
|
||||||
|
[_,x,o]],*/
|
||||||
|
/*joueur_initial(J),*/
|
||||||
|
J = x,
|
||||||
|
negamax(J, I, 1, Pmax, [B, V]).
|
||||||
|
|
||||||
|
cpu_time(Goal, Elapsed_Time) :-
|
||||||
|
statistics(process_cputime,Start),
|
||||||
|
call(Goal),
|
||||||
|
statistics(process_cputime,Finish),
|
||||||
|
Elapsed_Time is Finish-Start.
|
||||||
|
|
||||||
|
/*
|
||||||
|
A FAIRE :
|
||||||
|
Compléter puis tester le programme principal pour plusieurs valeurs de la profondeur maximale.
|
||||||
|
Pmax = 1, 2, 3, 4 ...
|
||||||
|
Commentez les résultats obtenus.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*jouer_adv(I, N) :-
|
||||||
|
|
||||||
|
jouer_*/
|
314
TP2/Paul_Optimise_et_Jeu/tictactoe.pl
Normal file
314
TP2/Paul_Optimise_et_Jeu/tictactoe.pl
Normal file
|
@ -0,0 +1,314 @@
|
||||||
|
/*********************************
|
||||||
|
DESCRIPTION DU JEU DU TIC-TAC-TOE
|
||||||
|
*********************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Une situation est decrite par une matrice 3x3.
|
||||||
|
Chaque case est soit un emplacement libre (Variable LIBRE), soit contient le symbole d'un des 2 joueurs (o ou x)
|
||||||
|
|
||||||
|
Contrairement a la convention du tp precedent, pour modeliser une case libre
|
||||||
|
dans une matrice on n'utilise pas une constante speciale (ex : nil, 'vide', 'libre','inoccupee' ...);
|
||||||
|
On utilise plutôt un identificateur de variable, qui n'est pas unifiee (ex : X, A, ... ou _) .
|
||||||
|
La situation initiale est une "matrice" 3x3 (liste de 3 listes de 3 termes chacune)
|
||||||
|
où chaque terme est une variable libre.
|
||||||
|
Chaque coup d'un des 2 joureurs consiste a donner une valeur (symbole x ou o) a une case libre de la grille
|
||||||
|
et non a deplacer des symboles deja presents sur la grille.
|
||||||
|
|
||||||
|
Pour placer un symbole dans une grille S1, il suffit d'unifier une des variables encore libres de la matrice S1,
|
||||||
|
soit en ecrivant directement Case=o ou Case=x, ou bien en accedant a cette case avec les predicats member, nth1, ...
|
||||||
|
La grille S1 a change d'etat, mais on n'a pas besoin de 2 arguments representant la grille avant et apres le coup,
|
||||||
|
un seul suffit.
|
||||||
|
Ainsi si on joue un coup en S, S perd une variable libre, mais peut continuer a s'appeler S (on n'a pas besoin de la designer
|
||||||
|
par un nouvel identificateur).
|
||||||
|
*/
|
||||||
|
|
||||||
|
situation_initiale([ [_,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_] ]).
|
||||||
|
|
||||||
|
% Convention (arbitraire) : c'est x qui commence
|
||||||
|
|
||||||
|
joueur_initial(x).
|
||||||
|
|
||||||
|
|
||||||
|
% Definition de la relation adversaire/2
|
||||||
|
|
||||||
|
adversaire(x,o).
|
||||||
|
adversaire(o,x).
|
||||||
|
|
||||||
|
|
||||||
|
/****************************************************
|
||||||
|
DEFINIR ICI a l'aide du predicat ground/1 comment
|
||||||
|
reconnaitre une situation terminale dans laquelle il
|
||||||
|
n'y a aucun emplacement libre : aucun joueur ne peut
|
||||||
|
continuer a jouer (quel qu'il soit).
|
||||||
|
****************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
situation_terminale(_Joueur, Situation) :-
|
||||||
|
ground(Situation).
|
||||||
|
|
||||||
|
/***************************
|
||||||
|
DEFINITIONS D'UN ALIGNEMENT
|
||||||
|
***************************/
|
||||||
|
|
||||||
|
alignement(L, Matrix) :- ligne( L,Matrix).
|
||||||
|
alignement(C, Matrix) :- colonne( C,Matrix).
|
||||||
|
alignement(D, Matrix) :- diagonale(D,Matrix).
|
||||||
|
|
||||||
|
/********************************************
|
||||||
|
DEFINIR ICI chaque type d'alignement maximal
|
||||||
|
existant dans une matrice carree NxN.
|
||||||
|
********************************************/
|
||||||
|
|
||||||
|
ligne(L, M) :-
|
||||||
|
member(L, M).
|
||||||
|
|
||||||
|
colonne(C,M) :-
|
||||||
|
find_col_N(_, C, M).
|
||||||
|
|
||||||
|
find_col_N(_, [], []).
|
||||||
|
find_col_N(N, [X|C], [L|R]) :-
|
||||||
|
nth1(N, L, X),
|
||||||
|
find_col_N(N, C, R).
|
||||||
|
|
||||||
|
/* Definition de la relation liant une diagonale D a la matrice M dans laquelle elle se trouve.
|
||||||
|
il y en a 2 sortes de diagonales dans une matrice carree(https://fr.wikipedia.org/wiki/Diagonale) :
|
||||||
|
- la premiere diagonale (principale) : (A I)
|
||||||
|
- la seconde diagonale : (Z R)
|
||||||
|
A . . . . . . . Z
|
||||||
|
. \ . . . . . / .
|
||||||
|
. . \ . . . / . .
|
||||||
|
. . . \ . / . . .
|
||||||
|
. . . . X . . .
|
||||||
|
. . . / . \ . . .
|
||||||
|
. . / . . . \ . .
|
||||||
|
. / . . . . . \ .
|
||||||
|
R . . . . . . . I
|
||||||
|
*/
|
||||||
|
|
||||||
|
diagonale(D, M) :-
|
||||||
|
premiere_diag(1,D,M).
|
||||||
|
|
||||||
|
% deuxieme definition A COMPLETER
|
||||||
|
|
||||||
|
diagonale(D, M) :-
|
||||||
|
length(M, N),
|
||||||
|
deuxieme_diag(N,D,M).
|
||||||
|
|
||||||
|
|
||||||
|
premiere_diag(_,[],[]).
|
||||||
|
premiere_diag(K,[E|D],[Ligne|M]) :-
|
||||||
|
nth1(K,Ligne,E),
|
||||||
|
K1 is K+1,
|
||||||
|
premiere_diag(K1,D,M).
|
||||||
|
|
||||||
|
deuxieme_diag(0,[],[]).
|
||||||
|
deuxieme_diag(K,[E|D],[Ligne|M]) :-
|
||||||
|
nth1(K, Ligne, E),
|
||||||
|
K1 is K - 1,
|
||||||
|
deuxieme_diag(K1, D, M).
|
||||||
|
|
||||||
|
|
||||||
|
test_align(A) :-
|
||||||
|
alignement(A, [[a,b,c], [d,e,f], [g,h,i]]).
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************
|
||||||
|
DEFINITION D'UN ALIGNEMENT
|
||||||
|
POSSIBLE POUR UN JOUEUR DONNE
|
||||||
|
*****************************/
|
||||||
|
|
||||||
|
possible([X|L], J) :- unifiable(X,J), possible(L,J).
|
||||||
|
possible( [], _).
|
||||||
|
|
||||||
|
/* Attention
|
||||||
|
il faut juste verifier le caractere unifiable
|
||||||
|
de chaque emplacement de la liste, mais il ne
|
||||||
|
faut pas realiser l'unification.
|
||||||
|
*/
|
||||||
|
|
||||||
|
unifiable(X,J) :-
|
||||||
|
(var(X) ->
|
||||||
|
true
|
||||||
|
;
|
||||||
|
J == X
|
||||||
|
).
|
||||||
|
|
||||||
|
test_possible(L) :-
|
||||||
|
process_test_possible([[x,x,x],[_,_,_],[x,_,_],[o,o,o],[o,_,_],[o,_,x], [x,_,o]], L).
|
||||||
|
|
||||||
|
process_test_possible([], []).
|
||||||
|
process_test_possible([Test | Rest], [[Result, Test]| R]) :-
|
||||||
|
(possible(Test, x) ->
|
||||||
|
Result = true
|
||||||
|
;
|
||||||
|
Result = false
|
||||||
|
),
|
||||||
|
process_test_possible(Rest, R).
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
DEFINITION D'UN ALIGNEMENT GAGNANT
|
||||||
|
OU PERDANT POUR UN JOUEUR DONNE J
|
||||||
|
**********************************/
|
||||||
|
/*
|
||||||
|
Un alignement gagnant pour J est un alignement
|
||||||
|
possible pour J qui n'a aucun element encore libre.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
Remarque : le predicat ground(X) permet de verifier qu'un terme
|
||||||
|
prolog quelconque ne contient aucune partie variable (libre).
|
||||||
|
exemples :
|
||||||
|
?- ground(Var).
|
||||||
|
no
|
||||||
|
?- ground([1,2]).
|
||||||
|
yes
|
||||||
|
?- ground(toto(nil)).
|
||||||
|
yes
|
||||||
|
?- ground( [1, toto(nil), foo(a,B,c)] ).
|
||||||
|
no
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Un alignement perdant pour J est un alignement gagnant pour son adversaire. */
|
||||||
|
|
||||||
|
alignement_gagnant(Ali, J) :-
|
||||||
|
ground(Ali),
|
||||||
|
possible(Ali, J).
|
||||||
|
|
||||||
|
alignement_perdant(Ali, J) :-
|
||||||
|
ground(Ali),
|
||||||
|
adversaire(J, Ad),
|
||||||
|
possible(Ali, Ad).
|
||||||
|
|
||||||
|
|
||||||
|
test_ali_gagn_perd(L) :-
|
||||||
|
process_test_ali_gagn_perd([[x,x,x],[_,_,_],[x,_,_],[o,o,o],[o,_,_],[o,_,x], [x,_,o]], L).
|
||||||
|
|
||||||
|
process_test_ali_gagn_perd([], []).
|
||||||
|
process_test_ali_gagn_perd([Test | Rest], [[ResultG, ResultP, Test]| R]) :-
|
||||||
|
(alignement_gagnant(Test, x) ->
|
||||||
|
ResultG = true
|
||||||
|
;
|
||||||
|
ResultG = false
|
||||||
|
),
|
||||||
|
(alignement_perdant(Test, x) ->
|
||||||
|
ResultP = true
|
||||||
|
;
|
||||||
|
ResultP = false
|
||||||
|
),
|
||||||
|
process_test_ali_gagn_perd(Rest, R).
|
||||||
|
|
||||||
|
|
||||||
|
/* ****************************
|
||||||
|
DEFINITION D'UN ETAT SUCCESSEUR
|
||||||
|
****************************** */
|
||||||
|
|
||||||
|
/*
|
||||||
|
Il faut definir quelle operation subit la matrice
|
||||||
|
M representant l'Etat courant
|
||||||
|
lorsqu'un joueur J joue en coordonnees [L,C]
|
||||||
|
*/
|
||||||
|
|
||||||
|
successeur(J, Etat,[L,C]) :-
|
||||||
|
nth1(L, Etat, Ligne),
|
||||||
|
nth1(C, Ligne, X),
|
||||||
|
var(X),
|
||||||
|
X = J.
|
||||||
|
|
||||||
|
/**************************************
|
||||||
|
EVALUATION HEURISTIQUE D'UNE SITUATION
|
||||||
|
**************************************/
|
||||||
|
|
||||||
|
/*
|
||||||
|
1/ l'heuristique est +infini si la situation J est gagnante pour J
|
||||||
|
2/ l'heuristique est -infini si la situation J est perdante pour J
|
||||||
|
3/ sinon, on fait la difference entre :
|
||||||
|
le nombre d'alignements possibles pour J
|
||||||
|
moins
|
||||||
|
le nombre d'alignements possibles pour l'adversaire de J
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :- % cas 1
|
||||||
|
H = 10000, % grand nombre approximant +infini
|
||||||
|
alignement(Alig,Situation),
|
||||||
|
alignement_gagnant(Alig,J), !.
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :- % cas 2
|
||||||
|
H = -10000, % grand nombre approximant -infini
|
||||||
|
alignement(Alig,Situation),
|
||||||
|
alignement_perdant(Alig,J), !.
|
||||||
|
|
||||||
|
|
||||||
|
% on ne vient ici que si les cut precedents n'ont pas fonctionne,
|
||||||
|
% c-a-d si Situation n'est ni perdante ni gagnante.
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :- % cas 3
|
||||||
|
findall(Alig, alignement(Alig, Situation), L),
|
||||||
|
count_H(L, J, 0, H).
|
||||||
|
|
||||||
|
count_H([], _, H, H).
|
||||||
|
count_H([Alig | Rest], J, LocalH, H) :-
|
||||||
|
adversaire(J, Ad),
|
||||||
|
(possible(Alig, J) ->
|
||||||
|
(possible(Alig, Ad) ->
|
||||||
|
count_H(Rest, J, LocalH, H)
|
||||||
|
;
|
||||||
|
NewH is LocalH + 1,
|
||||||
|
count_H(Rest, J, NewH, H)
|
||||||
|
)
|
||||||
|
;
|
||||||
|
(possible(Alig, Ad) ->
|
||||||
|
NewH is LocalH - 1,
|
||||||
|
count_H(Rest, J, NewH, H)
|
||||||
|
;
|
||||||
|
count_H(Rest, J, LocalH, H)
|
||||||
|
)
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test_heuristique(L) :-
|
||||||
|
process_test_heuristique([ [[_,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[x,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[_,_,_],
|
||||||
|
[_,x,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[x,x,x],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[o,_,_],
|
||||||
|
[_,o,_],
|
||||||
|
[_,_,o]],
|
||||||
|
|
||||||
|
[[o,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[_,_,_],
|
||||||
|
[_,o,_],
|
||||||
|
[_,_,_]],
|
||||||
|
|
||||||
|
[[x,_,_],
|
||||||
|
[o,_,o],
|
||||||
|
[_,_,x]] ], L).
|
||||||
|
|
||||||
|
process_test_heuristique([], []).
|
||||||
|
process_test_heuristique([Test | Rest], [[H]| R]) :-
|
||||||
|
heuristique(x, Test, H),
|
||||||
|
process_test_heuristique(Rest, R).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue