﻿%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%       Vous pouvez tester une partie entre deux joueurs alÃ©atoire de la faÃ§on suivante:
%       lancer_partie.
%       Le nombre de lignes/colonnes du damier va vous Ãªtre demandÃ©. Entrez un nombre pair positif.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

/*
        Permet de lancer une partie.
        Demande le nombre de lignes/colonnes Ã  l'utilisateur.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
lancer_partie :-        init_plateau(N1, P),
                        joue_partie(0, noir, P, _).

lancer_partie :-        write('Nombre de lignes incorrect.'), !, fail.
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Initialise un plateau de jeu.
        N=nombre de (lignes ou colonnes)/2
        Le plateau de jeu doit Ãªtre carrÃ© avec un nombre pair de lignes/colonnes.
        Structure d'une case: [Colonne, Ligne]
        Par exemple, la case [-3, 3] est la case en haut Ã  gauche du plateau et [3, 3] celle en bas Ã  droite.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
init_plateau(N, [N, [[-1,1], [1,-1]], [[1,1], [-1,-1]]]).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Cette fonction renvoie la valeur de l'indice supÃ©rieur Ã  celui reÃ§u en argument en tenant compte de l'ordre suivant: -3, -2, -1, 1, 2, 3
        voisin_superieur(1, -Y): associe 2 Ã  Y car 2 est le voisin supÃ©rieur de 1.
        voisin_superieur(-X, 2): associe 1 Ã  X car 1 est le voisin infÃ©rieur de 2 puisque 2 est le voisin supÃ©rieur de 1.
        voisin_superieur(1, 2) = true car 2 est aprÃ¨s 1 (attention Ã  voisin_superieur(-1, 1) qui est true Ã©galement).
        voisin_superieur(1, 3) = false car le voisin supÃ©rieur de 1 est 2 et non 3.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
voisin_superieur(_, -1, 1) :- !.
voisin_superieur(N, X, Y) :-    (integer(X),
                                        X < N,
                                        N1 is N*(-1),
                                        (X > N1; X == N1), !,
                                        Y is X + 1);
                                        (integer(Y),
                                        (Y < N; Y == N),
                                        N1 is N*(-1),
                                        Y > N1, !,
                                        X is Y - 1).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        On peut se dÃ©placer dans tous les sens. Nous avons donc dÃ©cidÃ© d'utiliser les noms des points cardinaux pour plus de simplicitÃ©.
        Cette fonction permet de rÃ©cupÃ©rer les coordonnÃ©es de la case voisine suivant une direction donnÃ©e parmi les points cardinaux:
        - nord
        - nordEst
        - est
        - sudEst
        - sud
        - sudOuest
        - ouest
        - nordOuest
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
case_voisine(N, [Xd, Yd], est, [Xa, Yd]) :- voisin_superieur(N, Xd, Xa).
case_voisine(N, [Xd, Yd], sudEst, [Xa, Ya]) :- voisin_superieur(N, Xd, Xa), voisin_superieur(N, Ya, Yd).
case_voisine(N, [Xd, Yd], sud, [Xd, Ya]) :- voisin_superieur(N, Ya, Yd).
case_voisine(N, [Xd, Yd], sudOuest, [Xa, Ya]) :- voisin_superieur(N, Xa, Xd), voisin_superieur(N, Ya, Yd).
case_voisine(N, [Xd, Yd], ouest, [Xa, Yd]) :- voisin_superieur(N, Xa, Xd).
case_voisine(N, [Xd, Yd], nordOuest, [Xa, Ya]) :- voisin_superieur(N, Xa, Xd), voisin_superieur(N, Yd, Ya).
case_voisine(N, [Xd, Yd], nord, [Xd, Ya]) :- voisin_superieur(N, Yd, Ya).
case_voisine(N, [Xd, Yd], nordEst, [Xa, Ya]) :- voisin_superieur(N, Xd, Xa), voisin_superieur(N, Yd, Ya).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Permet de rÃ©cupÃ©rer la couleur de l'adversaire.
        couleur_adversaire(+C1, -C2): Affecte Ã  C2 la couleur de l'adversaire de C1.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
couleur_adversaire(noir, blanc).
couleur_adversaire(blanc, noir).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Permet de rÃ©cupÃ©rer la liste des pions d'un joueur suivant sa couleur.
        pions_joueur(+Couleur, +Plateau, -Pions): Affecte Ã  Pions la liste des pions du joueur dont la couleur est Couleur.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
pions_joueur(noir, [_, X, _], X).
pions_joueur(blanc, [_, _, X], X).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Construction de la liste des coups lÃ©gaux. Pour ce faire, nous allons observer toutes les cases contenant des pions du joueur adverse et voir si ces pions peuvent Ãªtre mangÃ©s.
        Structure d'un coup:
        [Case, [Liste des cases que ce coup retournera s'il est jouÃ©]]
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
tous_coups_legaux(Couleur, [N, PN, PB], R) :-   pions_joueur(Couleur, [N, PN, PB], Pions),
                                                couleur_adversaire(Couleur, CAdverse),
                                                pions_joueur(CAdverse, [N, PN, PB], PAdverse),
                                                tous_coups_legaux1(Couleur, N, Pions, PAdverse, PAdverse, [], [], R).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Appel successivement coups_legaux avec les pions de l'adversaire. DÃ¨s que l'on en a terminÃ© avec un pion, on passe au suivant. Lorsque la liste des pions Ã  explorer est vide, on termine.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
tous_coups_legaux1(Couleur, N, P, PA, [X|Y], T, A, R) :-        coups_legaux(N, X, P, PA, T, [], T1, R1),
                                                                append(R1, A, A1),
                                                                tous_coups_legaux1(Couleur, N, P, PA, Y, T1, A1, R).
tous_coups_legaux1(_, _, _, _, [], _, A, A). % Transfert de l'accumulateur dans la variable de rÃ©sultat.
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Recherche tous les coups lÃ©gaux que l'on peut faire Ã  partir d'un pion du joueur adverse.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
coups_legaux(N, Case, P, PA, T, A, TR, R) :-    case_voisine(N, Case, _, NCase),
                                                        not(member(NCase, T)), !,
                                                        jetons_retournes(N, NCase, P, PA, [], [], R1),
                                                        (not_empty(R1)->
                                                                coups_legaux(N, Case, P, PA, [NCase|T], [[NCase,R1]|A], TR, R);
                                                                coups_legaux(N, Case, P, PA, [NCase|T], A, TR, R)).
coups_legaux(_, _, _, _, T, A, T, A).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Construction de la liste des pions qu'il faudra retourner si le coup est jouÃ©. Si un coup n'est pas lÃ©gal, sa liste de pions Ã  retourner est vide.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
jetons_retournes(N, Case, P, PA, T, A, R) :-    not(member(Case, P)),
                                                        not(member(Case, PA)),
                                                        case_voisine(N, Case, Direction, NCase),
                                                        member(NCase, PA),
                                                        not(member(NCase, T)),
                                                        sandwich(N, NCase, Direction, P, PA, [NCase], R1), !,
                                                        append(R1, A, A1),
                                                        jetons_retournes(N, Case, P, PA, [NCase|T], A1, R).
jetons_retournes(_, _, _, _, _, A, A).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Construit la liste des pions Ã  retourner pour un coup suivant une certaine direction. Les directions sont donnÃ©es par jetons_retournes.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
sandwich(_, _, _, [], [], A, A) :- !.
sandwich(N, Case, Direction, P, PA, A, R) :-    case_voisine(N, Case, Direction, NCase),
                                                                (member(NCase, PA)->
                                                                (!, sandwich(N, NCase, Direction, P, PA, [NCase|A], R));
                                                                (!, member(NCase, P), !, sandwich(N, Case, Direction, [], [], A, R))).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        SÃ©lectionne alÃ©atoirement un coup Ã  jouer parmi tous les coup lÃ©gaux suivant une couleur et un plateau de jeu.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
select_coup(Couleur, Plateau, Coup) :-  tous_coups_legaux(Couleur, Plateau, L),
                                        length(L, NbCoups),
                                        NbCoups > 0, !,
                                        Index is random(NbCoups),
                                        nth0(Index, L, Coup).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Joue un coup donnÃ© dans le plateau courant.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
joue_coup(Couleur, Plateau, [Case,CasesR], NPlateau) :- ajouter_pions(Couleur, Plateau, [Case|CasesR], NPlateau1),
                                                                couleur_adversaire(Couleur, CAdverse),
                                                                retirer_pions(CAdverse, NPlateau1, CasesR, NPlateau).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Ajoute une liste de pions dans la liste des pions d'un certain joueur suivant sa couleur.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
ajouter_pions(noir, [N, PN, PB], Pions, [N, PNB, PB]) :- append(Pions, PN, PNB).
ajouter_pions(blanc, [N, PN, PB], Pions, [N, PN, PBB]) :- append(Pions, PB, PBB).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Retire une liste de pions dans la liste des pions d'un certain joueur suivant sa couleur.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
retirer_pions(noir, [N, PN, PB], Pions, [N, PNB, PB]) :- delete_multiple(PN, Pions, PNB).
retirer_pions(blanc, [N, PN, PB], Pions, [N, PN, PBB]) :- delete_multiple(PB, Pions, PBB).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Permet de savoir si une liste est non vide.
        Il faut Ãªtre sÃ»r que l'Ã©lÃ©ment donnÃ© en argument est une liste. Aucun test n'est effectuÃ© dans la fonction.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
not_empty([]) :- !, fail.
not_empty(_).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Supprime une liste d'Ã©lÃ©ments dans une liste. MÃªme principe que delete mais avec une liste d'Ã©lÃ©ments et non un singleton.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
delete_multiple(L, [], L).
delete_multiple(L, [X|Y], R) :- delete(L, X, L1),
                                delete_multiple(L1, Y, R).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Joue une partie avec 2 joueurs alÃ©atoires tant qu'il existe un coup lÃ©gal.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
joue_partie(2, _, [N, PN, PB], [N, PN, PB]) :-
        !,
        write('FIN DE LA PARTIE d='')\n'),
        length(PN, LN),
        length(PB, LB),
        format('Noir a ~a pions et Blanc en a ~a.\n', [LN, LB]),
        (LN == LB ->
                write('EgalitÃ©!');
                ((LN > LB ->
                        write('Noir');
                        write('Blanc')),
                write(' est le grand gagnant! Il est trop fort Ã  Othello :P\n'))).

joue_partie(_, Couleur, Plateau, NPlateau) :-
        write('Au tour de '),
        write(Couleur),
        write(' de jouer:\n'),
        afficher_plateau(Plateau),
        select_coup(Couleur, Plateau, Coup),
        joue_coup(Couleur, Plateau, Coup, NPlateau1),
        couleur_adversaire(Couleur, CAdverse), !,
        joue_partie(0, CAdverse, NPlateau1, NPlateau).

joue_partie(NbPasses, Couleur, Plateau, NPlateau) :-
        !,
        NbPasses2 is NbPasses + 1,
        couleur_adversaire(Couleur, CAdverse),
        joue_partie(NbPasses2, CAdverse, Plateau, NPlateau).
        


%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Affiche le plateau de jeu reÃ§u.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
afficher_plateau([N, PN, PB]) :- N1 is N*(-1), afficher_plateau1(N1, N, [N, PN, PB]).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        MÃªme chose que affiche_plateau mais avec des arguments en plus pour savoir quelles sont la ligne et la colonne courante.
        Cette fonction est appelÃ©e par afficher_plateau avec au dÃ©part C=-N et L=N.
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
% Fin de l'affichage.
afficher_plateau1(N, N1, [N, PN, PB]) :-        N1 is N*(-1), !, afficher_case([N, N1], PN, PB), write('\n\n').
% Affichage d'une case en fin de colonne => saut de ligne.
afficher_plateau1(N, L, [N, PN, PB]) :- afficher_case([N, L], PN, PB),
                                                        write('\n'),
                                                        voisin_superieur(N, L1, L), !,
                                                        C1 is N*(-1),
                                                        afficher_plateau1(C1, L1, [N, PN, PB]).
% Affichage d'une case normale.
afficher_plateau1(C, L, [N, PN, PB]) :- afficher_case([C, L], PN, PB),
                                                        voisin_superieur(N, C, C1),
                                                        afficher_plateau1(C1, L, [N, PN, PB]).
%%%%% %%%%% %%%%% %%%%% %%%%%

/*
        Affiche une case du plateau de jeu.
        Pion noir = x
        Pion blanc = o
        Case vide = _
*/
%%%%% %%%%% %%%%% %%%%% %%%%%
afficher_case(C, PN, PB) :-     write(' '),
                                        (member(C, PN) ->
                                        write('x');
                                        (member(C, PB) ->
                                                write('o');
                                                write('_'))).
%%%%% %%%%% %%%%% %%%%% %%%%%


% trace, init_plateau(3, P), afficher_plateau(P).
% trace, init_plateau(3, P), tous_coups_legaux(noir, P, R).
% init_plateau(3, P), joue_partie(noir, P, NP).
