TP2 version fonctionnelle
This commit is contained in:
parent
68f191c7da
commit
7e31c799b2
1 changed files with 428 additions and 0 deletions
428
TP2.pl
Normal file
428
TP2.pl
Normal file
|
@ -0,0 +1,428 @@
|
||||||
|
/*********************************
|
||||||
|
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<EFBFBD>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<EFBFBD> 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).
|
||||||
|
*/
|
||||||
|
%:- use_module(library(clpfd)).
|
||||||
|
|
||||||
|
situation_initiale([ [_,_,_],
|
||||||
|
[_,_,_],
|
||||||
|
[_,_,_]]).
|
||||||
|
|
||||||
|
% Convention (arbitraire) : c'est x qui commence
|
||||||
|
|
||||||
|
joueur_initial(x).
|
||||||
|
|
||||||
|
cpu_time(Goal, Elapsed_Time) :-
|
||||||
|
statistics(process_cputime,Start),
|
||||||
|
call(Goal),
|
||||||
|
statistics(process_cputime,Finish),
|
||||||
|
Elapsed_Time is (Finish-Start)*1000.
|
||||||
|
|
||||||
|
% 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_aux(_,[],[]).
|
||||||
|
|
||||||
|
colonne_aux(N,[X|Cr],[L|Mr]):-
|
||||||
|
nth1(N,L,X),
|
||||||
|
colonne_aux(N,Cr,Mr).
|
||||||
|
|
||||||
|
colonne(C,M) :-
|
||||||
|
colonne_aux(_,C,M).
|
||||||
|
|
||||||
|
%colonne(C,M) :- transpose(M,T), member(C,T).
|
||||||
|
|
||||||
|
/* 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) :- seconde_diag(3,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) -> true ; J=X) .
|
||||||
|
|
||||||
|
/**********************************
|
||||||
|
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) :- possible(Ali,J), ground(Ali).
|
||||||
|
|
||||||
|
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,Lig), nth1(C,Lig,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.
|
||||||
|
|
||||||
|
% A FAIRE cas 3
|
||||||
|
|
||||||
|
heuristique(J,Situation,H) :-
|
||||||
|
findall(X, (alignement(X,Situation),possible(X,J)),Lgagnant),
|
||||||
|
adversaire(J,A),
|
||||||
|
findall(Y, (alignement(Y,Situation),possible(Y,A)),Lperdant),
|
||||||
|
length(Lgagnant, LG),
|
||||||
|
length(Lperdant, LP),
|
||||||
|
H is LG-LP.
|
||||||
|
|
||||||
|
test_heuristique :-
|
||||||
|
( ( S1= [ [_,_,_], [_,_,_], [_,_,_] ],
|
||||||
|
heuristique(x,S1,0),
|
||||||
|
S2 = [ [x,o,_], [_,_,_], [_,_,_] ],
|
||||||
|
heuristique(x,S2,1),
|
||||||
|
S3 = [ [_,o,_], [_,x,_], [_,_,_] ],
|
||||||
|
heuristique(x,S3,2),
|
||||||
|
S4 = [ [o,_,_], [_,x,_], [_,_,_] ],
|
||||||
|
heuristique(x,S4,1),
|
||||||
|
S5 = [ [x,_,_], [_,o,_], [_,_,_] ],
|
||||||
|
heuristique(o,S5,1),
|
||||||
|
S6 = [ [x,_,_], [_,x,_], [_,_,x] ],
|
||||||
|
heuristique(o,S6,-10000),
|
||||||
|
S7 = [ [x,_,_], [_,x,_], [_,_,x] ],
|
||||||
|
heuristique(x,S7,10000)
|
||||||
|
) ->
|
||||||
|
write('test heuristique successful')
|
||||||
|
;
|
||||||
|
write('error heuristique test')
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*******************************************
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* NEGAMAX
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* *****************************************/
|
||||||
|
/*
|
||||||
|
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, [nil, Val]) :- heuristique(J,Etat,Val).
|
||||||
|
|
||||||
|
negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
|
||||||
|
heuristique(J,Etat,Val),
|
||||||
|
Val = 10000.
|
||||||
|
|
||||||
|
negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
|
||||||
|
heuristique(J,Etat,Val),
|
||||||
|
Val = -10000.
|
||||||
|
|
||||||
|
negamax(J,Etat, P, Pmax, [nil,Val]) :- P < Pmax ,
|
||||||
|
situation_terminale(J,Etat),
|
||||||
|
heuristique(J,Etat,Val).
|
||||||
|
|
||||||
|
|
||||||
|
negamax(J,Etat, P, Pmax, [Coup,Val]) :- P < Pmax ,
|
||||||
|
successeurs(J,Etat,Successeurs),
|
||||||
|
loop_negamax(J,P,Pmax,Successeurs,Liste_Couples),
|
||||||
|
meilleur(Liste_Couples,[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([Cm,Vm], [Cm,Vm]).
|
||||||
|
meilleur([[C,V]|R], [Cm,Vm]) :-
|
||||||
|
meilleur(R, [Cm,Vm]),
|
||||||
|
( Vm > V -> ).*/
|
||||||
|
meilleur([X], X). %le meilleur dans une liste a un seul element est cet element
|
||||||
|
meilleur(Liste_de_Couples, MeilleurCouple ) :-
|
||||||
|
Liste_de_Couples = [ [C,V] | Fin ] ,
|
||||||
|
meilleur(Fin,[Cout,Val]),
|
||||||
|
|
||||||
|
( Val > V -> MeilleurCouple = [C , V]
|
||||||
|
;
|
||||||
|
MeilleurCouple = [Cout , Val]
|
||||||
|
).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
test_meilleur :-
|
||||||
|
( ( meilleur([[a,2],[b,3],[c,4] ],[a,2]),
|
||||||
|
meilleur([[a,2],[b,3],[c,1] ],[c,1]),
|
||||||
|
meilleur([[a,2],[b,2]],[b,2]),
|
||||||
|
meilleur([[a,2]],[a,2])
|
||||||
|
) ->
|
||||||
|
write('test meilleur successful')
|
||||||
|
;
|
||||||
|
write('error meilleur test')
|
||||||
|
).
|
||||||
|
|
||||||
|
/******************
|
||||||
|
PROGRAMME PRINCIPAL
|
||||||
|
*******************/
|
||||||
|
|
||||||
|
main(B,V, Pmax) :-
|
||||||
|
situation_initiale(Ini),
|
||||||
|
joueur_initial(J),
|
||||||
|
negamax(J,Ini,0,Pmax,[B,V]).
|
||||||
|
|
Loading…
Reference in a new issue