Ajout des fichiers sources prolog

This commit is contained in:
Paul Faure 2021-03-21 19:11:10 +01:00
commit bbf58794f4
8 changed files with 1800 additions and 0 deletions

134
TP1/aetoile.pl Normal file
View 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
View 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
View 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).

View 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.

View 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)
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.

View 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").

View 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_*/

View 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)
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).