Prolog-TP-IA/TP2/tictactoe.pl
2023-02-27 23:00:46 +01:00

244 lines
6.7 KiB
Prolog
Raw Blame History

/*********************************
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<75>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).
:- situation_terminale(o,[[o,o,o],[x,x,x],[o,x,o]]).
:- not(situation_terminale(o,[[o,o,o],[x,x,x],[o,x,_]])).
/***************************
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([],[],_).
colonne([E|C],[L|M],Nb) :-
nth1(Nb,L,E),
colonne(C,M,Nb).
/* 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).
diagonale(D, M) :-
length(M, L),
seconde_diag(L,D,M).
% Definition de la premiere diagonale (partant de (1,1) pour aller vers (NxN))
premiere_diag(_,[],[]).
premiere_diag(K,[E|D],[Ligne|M]) :-
nth1(K,Ligne,E),
K1 is K+1,
premiere_diag(K1,D,M).
% Definition de la deuxieme diagonale (partant de (1,N) pour aller vers (N,1))
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.
*/
% On regarde si la variable X est libre ou si elle contient le symbole du joueur
unifiable(X,_):-
var(X), !.
unifiable(X,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. */
alignement_gagnant(Ali, J) :-
ground(Ali),
adversaire(J, Adv),
not(member(Adv, Ali)).
alignement_perdant(Ali, J) :-
ground(Ali),
not(member(J, Ali)).
% Quelques tests
:- alignement_gagnant([x,x,x], x).
:- not(alignement_gagnant([x,x,o], x)).
:- not(alignement_gagnant([x,x,_], x)).
:- not(alignement_perdant([x,x,_], x)).
:- not(alignement_perdant([x,x,x], x)).
:- alignement_perdant([o,o,o], x).
/* ****************************
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, Emplacement),
var(Emplacement),
nth1(L, Etat, Ligne),
nth1(C, Ligne, 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
*/
% Cas gagant
heuristique(J,Situation,H) :-
H = 10000, % grand nombre approximant +infini
alignement(Alig,Situation),
alignement_gagnant(Alig,J), !.
% Cas perdant
heuristique(J,Situation,H) :-
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.
% Cas général
heuristique(J,Situation,H) :-
adversaire(J,Adv),
findall(AligX,
(alignement(AligX,Situation),possible(AligX,J)),
CountX),
findall(AligY,
(alignement(AligY,Situation), possible(AligY,Adv)),
CountY),
length(CountX,LX),
length(CountY,LY),
H is LX - LY.
% Quelques tests
:- A=[[o,_,_],[_,o,_],[_,_,_]],heuristique(o,A,6).
:- A=[[o,_,_],[_,_,_],[_,_,_]],heuristique(x,A,-3).
:- A=[[_,_,_],[_,_,_],[_,_,_]],heuristique(x,A,0).
:- A=[[o,_,_],[o,o,o],[_,_,_]],heuristique(o,A,10000).