:-use_module(library(clpfd)).
:- assert(lookahead(2)).
:- dynamic spy/0.  % debug calls to alpha_beta
:- assert(spy).    % Comment out stop spy.


/* init7(Coul,Jeu),successeurs(Jeu,Coul,L),length(L,N),aff_coup_possible(L). 
init3(Coul,Jeu),successeurs(Jeu,Coul,L),length(L,N),aff_coup_possible(L).
init5(Coul,Jeu),successeurs(Jeu,Coul,L),length(L,N),aff_coup_possible(L). */

/***************/
/* aritmétique */
/***************/

abs(X,X):-
    X >= 0. 
abs(X,A):-
    A is 0 - X.

min(X,Y,X):-
    X =< Y. 
min(_,Y,Y). 

max(X,Y,X):-
    X > Y .
max(_,Y,Y). 



/* types de case 
  v : vide
  b : blanc
  n : noire
  bb: blanc + balle 
  nb: noire + balle
*/
    

/*****************************/
/* Colonnes, lignes et cases */
/*****************************/

/* accesseurs sur ligne et colonne*/

ligcol(1,[A,_,_,_,_,_,_],A).
ligcol(2,[_,A,_,_,_,_,_],A).
ligcol(3,[_,_,A,_,_,_,_],A).
ligcol(4,[_,_,_,A,_,_,_],A).
ligcol(5,[_,_,_,_,A,_,_],A).
ligcol(6,[_,_,_,_,_,A,_],A).
ligcol(7,[_,_,_,_,_,_,A],A).

ligcol(1,[A,_,_,_,_],A).
ligcol(2,[_,A,_,_,_],A).
ligcol(3,[_,_,A,_,_],A).
ligcol(4,[_,_,_,A,_],A).
ligcol(5,[_,_,_,_,A],A).

ligcol(1,[A,_,_],A).
ligcol(2,[_,A,_],A).
ligcol(3,[_,_,A],A).








/* retourne le contenu "Contenu_case" de la case  
situé à la colonne "Col" et la ligne "Lig" du jeux en cours
"Contenu_jeu" */
 
case(Lig,Col,Contenu_jeu,Contenu_case) :- 
    ligcol(Lig,Contenu_jeu,Ligne),
    ligcol(Col,Ligne,Contenu_case).




changeContenuLigCol(1,X,[_,B,C,D,E,F,G],[X,B,C,D,E,F,G]).
changeContenuLigCol(2,X,[A,_,C,D,E,F,G],[A,X,C,D,E,F,G]).
changeContenuLigCol(3,X,[A,B,_,D,E,F,G],[A,B,X,D,E,F,G]).
changeContenuLigCol(4,X,[A,B,C,_,E,F,G],[A,B,C,X,E,F,G]).
changeContenuLigCol(5,X,[A,B,C,D,_,F,G],[A,B,C,D,X,F,G]).
changeContenuLigCol(6,X,[A,B,C,D,E,_,G],[A,B,C,D,E,X,G]).
changeContenuLigCol(7,X,[A,B,C,D,E,F,_],[A,B,C,D,E,F,X]).

changeContenuLigCol(1,X,[_,B,C,D,E],[X,B,C,D,E]).
changeContenuLigCol(2,X,[A,_,C,D,E],[A,X,C,D,E]).
changeContenuLigCol(3,X,[A,B,_,D,E],[A,B,X,D,E]).
changeContenuLigCol(4,X,[A,B,C,_,E],[A,B,C,X,E]).
changeContenuLigCol(5,X,[A,B,C,D,_],[A,B,C,D,X]).

changeContenuLigCol(1,X,[_,B,C],[X,B,C]).
changeContenuLigCol(2,X,[A,_,C],[A,X,C]).
changeContenuLigCol(3,X,[A,B,_],[A,B,X]).





changeContenu(Conf1,Piece,Y,X,Conf2):-
    ligcol(Y,Conf1,Ligne),
    changeContenuLigCol(X,Piece,Ligne,Lignep),
    changeContenuLigCol(Y,Lignep,Conf1,Conf2).



    
sans_balle(b).
sans_balle(n).


/*********************/
/* position initiale */
/*********************/

init7(b,
     [[b,b,b,bb,b,b,b],	
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v], 
      [n,n,n,nb,n,n,n]]).

init7_2(b,
     [[b,n,b,bb,b,n,b],	
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v], 
      [n,b,n,nb,n,b,n]]).

init7_test1(b,
     [[v,n,v,v,v,n,b],	
      [v,v,v,v,v,v,v],
      [b,v,b,b,b,bb,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,n,b,n], 
      [n,v,n,nb,v,v,v]]).


init7_test_victoire(b,
     [[v,n,v,v,v,n,b],	
      [v,v,v,v,v,v,v],
      [b,v,b,b,b,b,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,n,b,n], 
      [n,bb,n,nb,v,v,v]]).

init7_test_victoire2(b,
     [[v,n,v,v,v,nb,b],	
      [v,v,v,v,v,v,v],
      [b,v,b,v,b,b,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,v,v,v],
      [v,v,v,v,n,b,n], 
      [n,bb,n,n,v,v,v]]).


init5(b,
     [[b,b,bb,b,b],	
      [v,v,v,v,v],
      [v,v,v,v,v],
      [v,v,v,v,v], 
      [n,n,nb,n,n]]).

init5_2(b,
     [[n,b,bb,b,n],	
      [v,v,v,v,v],
      [v,v,v,v,v],
      [v,v,v,v,v], 
      [b,n,nb,n,b]]).


init3(b,
     [[b,bb,b],	
      [v,v,v],
      [n,nb,n]]).

init3_2(b,
     [[n,bb,n],	
      [v,v,v],
      [b,nb,b]]).





/* affichage */

affligne:-write('-----------------------------'),nl.

affiche(Jeu):-
    affligne,
    affiche2(Jeu).

affiche2([]).

affiche2([Prem_ligne|Reste]):-
    write('|'),affpions(Prem_ligne),nl,affligne,
    affiche2(Reste).

affpions([]).

affpions([b|R]):-
    write(' b |'),
    affpions(R).

affpions([v|R]):-
    write('   |'),
    affpions(R).

affpions([n|R]):-
    write(' n |'),
    affpions(R).

affpions([bb|R]):-
    write(' bb|'),
    affpions(R).

affpions([nb|R]):-
    write(' nb|'),
    affpions(R).




aff_coup_possible([]).

aff_coup_possible([P|R]):-
    affiche(P),
    aff_coup_possible(R).


/* victoire */


test_victoire(Coul,Jeu,true):-
		victoire(Coul,Jeu).

test_victoire(Coul,Jeu,false):-
		not(victoire(Coul,Jeu)).



victoire(n,Contenu_jeu):-            
    case(1,_,Contenu_jeu,nb).   

victoire(b,Contenu_jeu):-
    case(7,_,Contenu_jeu,bb).


/*************************/
/* deplacement possibles */
/*************************/

deplacement(0,_,Contenu_jeu,Contenu_jeu).


/*monter un */ 
deplacement(1,Coul,Contenu_jeu,Nouv_Contenu_jeu):-
    case(Lig,Col,Contenu_jeu,Coul),
    Ligp is Lig-1, Ligp >= 1, 
    case(Ligp,Col,Contenu_jeu,v),
		changeContenu(Contenu_jeu,v,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coul,Ligp,Col,Nouv_Contenu_jeu).


/*gauche un */ 
deplacement(1,Coul,Contenu_jeu,Nouv_Contenu_jeu):-
    case(Lig,Col,Contenu_jeu,Coul),
    Colp is Col-1, Colp >= 1, 
    case(Lig,Colp,Contenu_jeu,v),
		changeContenu(Contenu_jeu,v,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coul,Lig,Colp,Nouv_Contenu_jeu).




/* descendre */

depl(X,Coul,Contenu_jeu,Nouv_Contenu_jeu):-
		deplacement(X,Coul,Contenu_jeu,Nouv_Contenu_jeu).
depl(X,Coul,Contenu_jeu,Nouv_Contenu_jeu):-
		deplacement(X,Coul,Nouv_Contenu_jeu,Contenu_jeu).


/*************************/
/* lancer de balle       */
/*************************/

amis(n,n).

amis(n,v).
amis(b,b).

amis(b,v).


ma_couleur(b,b).
ma_couleur(b,bb).

ma_couleur(n,n).
ma_couleur(n,nb).

balon(n,nb).
balon(b,bb).

aucun_adversaire_ligne(_,_,_,_,0).
aucun_adversaire_ligne(Coul,Lig,Col,Contenu_jeu,Delta):-      
    Colp is Col + Delta, 
    case(Lig,Colp,Contenu_jeu,Coulp),
    amis(Coul,Coulp),
    Deltap is Delta -1,
    aucun_adversaire_ligne(Coul,Lig,Col,Contenu_jeu,Deltap).

aucun_adversaire_col(_,_,_,_,0).
aucun_adversaire_col(Coul,Lig,Col,Contenu_jeu,Delta):-
    Ligp is Lig + Delta, 
    case(Ligp,Col,Contenu_jeu,Coulp),
    amis(Coul,Coulp),
    Deltap is Delta -1,
    aucun_adversaire_col(Coul,Lig,Col,Contenu_jeu,Deltap).





sign(x,1):- x >= 0.
sign(x,-1):- x < 0.


aucun_adversaire_diag1(_,_,_,_,0).
aucun_adversaire_diag1(Coul,Lig,Col,Contenu_jeu,Delta):-
    Ligp is Lig + Delta, Colp is Col + Delta,
    case(Ligp,Colp,Contenu_jeu,Coulp),
    amis(Coul,Coulp),
		sign(Delta,s),
    Deltap is Delta -s,
    aucun_adversaire_diag1(Coul,Lig,Col,Contenu_jeu,Deltap).

aucun_adversaire_diag2(_,_,_,_,0).
aucun_adversaire_diag2(Coul,Lig,Col,Contenu_jeu,Delta):-
    Ligp is Lig + Delta, Colp is Col - Delta,
    case(Ligp,Colp,Contenu_jeu,Coulp),
    amis(Coul,Coulp),
		sign(Delta,s),
    Deltap is Delta -s,
    aucun_adversaire_diag2(Coul,Lig,Col,Contenu_jeu,Deltap).





/* horizontal */
lancerh(Coul,Contenu_jeu,Nouv_Contenu_jeu):-       
    balon(Coul,Coulb),
    case(Lig,Col,Contenu_jeu,Coulb),
    case(Lig,Colp,Contenu_jeu,Coul),
    min(Col,Colp,Min),
    max(Col,Colp,Max),
    Delta is Max-Min -1,
    aucun_adversaire_ligne(Coul,Lig,Min,Contenu_jeu,Delta),

		changeContenu(Contenu_jeu,Coul,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coulb,Lig,Colp,Nouv_Contenu_jeu).

/*    case(Lig,Col,Nouv_Contenu_jeu,Coul),
    case(Lig,Colp,Nouv_Contenu_jeu,Coulb).*/
    

/* vertical */
lancerv(Coul,Contenu_jeu,Nouv_Contenu_jeu):-
    balon(Coul,Coulb),
    case(Lig,Col,Contenu_jeu,Coulb),
    case(Ligp,Col,Contenu_jeu,Coul),
    min(Lig,Ligp,Min),
    max(Lig,Ligp,Max),
    Delta is Max-Min -1,
    aucun_adversaire_col(Coul,Lig,Min,Contenu_jeu,Delta),
   
		changeContenu(Contenu_jeu,Coul,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coulb,Ligp,Col,Nouv_Contenu_jeu).
		/*case(Lig,Col,Nouv_Contenu_jeu,Coul),
    case(Ligp,Col,Nouv_Contenu_jeu,Coulb).*/


/* diag haut droite */
lancerd1(Coul,Contenu_jeu,Nouv_Contenu_jeu):-
    balon(Coul,Coulb),
    case(Lig,Col,Contenu_jeu,Coulb),
    case(Ligp,Colp,Contenu_jeu,Coul),
    Delta is Ligp-Lig -1,
    Delta is Colp-Col -1,
    aucun_adversaire_diag1(Coul,Lig,Col,Contenu_jeu,Delta),
		
		changeContenu(Contenu_jeu,Coul,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coulb,Ligp,Colp,Nouv_Contenu_jeu).

    /*case(Lig,Col,Nouv_Contenu_jeu,Coul),
    case(Ligp,Colp,Nouv_Contenu_jeu,Coulb).*/
    




/* diag haut gauche */
lancerd2(Coul,Contenu_jeu,Nouv_Contenu_jeu):-
    balon(Coul,Coulb),
    case(Lig,Col,Contenu_jeu,Coulb),
    case(Ligp,Colp,Contenu_jeu,Coul),
    Delta is -(Ligp-Lig -1),
    Delta is Colp-Col -1,
    aucun_adversaire_diag2(Coul,Lig,Col,Contenu_jeu,Delta),
    
		changeContenu(Contenu_jeu,Coul,Lig,Col,Contenu_jeu1),
		changeContenu(Contenu_jeu1,Coulb,Ligp,Colp,Nouv_Contenu_jeu).

    /*case(Lig,Col,Nouv_Contenu_jeu,Coul),
    case(Ligp,Colp,Nouv_Contenu_jeu,Coulb).*/

    


lancer(0,_,Contenu_jeu,Contenu_jeu).

lancer(1,Coul,Contenu_jeu,Nouv_Contenu_jeu):-
		lancerv(Coul,Contenu_jeu,Nouv_Contenu_jeu);
    lancerh(Coul,Contenu_jeu,Nouv_Contenu_jeu);
    lancerd1(Coul,Contenu_jeu,Nouv_Contenu_jeu);
    lancerd2(Coul,Contenu_jeu,Nouv_Contenu_jeu).



coup(Coul,Contenu_Jeu,Nouv_Contenu_jeu):-
		lancer(L1,Coul,Contenu_Jeu,Contenu_jeu1),
		depl(D1,Coul,Contenu_jeu1,Contenu_jeu2),
		lancer(L2,Coul,Contenu_jeu2,Contenu_jeu3),
		depl(D2,Coul,Contenu_jeu3,Contenu_jeu4),
		lancer(L3,Coul,Contenu_jeu4,Nouv_Contenu_jeu),
		L is L1+L2+L3,
		L=<1,
		D is 	D1+D2,
		D =< 2.

/* tour */
joueur_suivant(n,b).
joueur_suivant(b,n).




/************ TODO à affiner **********/


valeur(Conf,1000):-
    victoire(b,Conf).

valeur(Conf,T):-
    victoire(n,Conf),
    T is -1000.

valeur(Conf,T):-
    valeur_b(Conf,T1),
    valeur_n(Conf,T2),
    T is T1 + T2.


valeur_b(Conf,T):- 
    maplist(nb_ele(b),Conf,L1),
    length(Conf,N),
    numlist(1,N,L2),
    maplist(poids_ligne,L1,L2,L3),
    sumlist(L3,T),!.


valeur_n(Conf,Tp):- 
    maplist(nb_ele(n),Conf,L1),
    length(Conf,N),
    numlist(1,N,L2),
    maplist(poids_ligne2(N),L1,L2,L3),
    sumlist(L3,T),
    Tp is -T,
    !.


    /* pour une ligne de poids X, de rang Y, son poids est N */
poids_ligne(X,Y,N):-
    N is 10*X*Y,!.


poids_ligne2(N,X,Y,R):-
    R is (N+1-Y)*10*X,!.

    /*nbre d elements N de la couleur Coul dans la liste L */
nb_ele(Coul,L,N):-
    sublist(ma_couleur(Coul),L,Lp),
    length(Lp,N).

successeurs(N,Coul,L) :- 
    findall(NP,coup(Coul,N,NP),Lp),
    list_to_set(Lp,Lpp),
    filtre_sym(Lpp,L),
    length(L,Taille),
    maplist(valeur,L,L2),
    write(L2),
    writef('successeurs de taille %w \n',[Taille]).

    /*
      choisit la meilleur conf à partir de Conf parmi les susccesseurs 
      alpha est la valeur minimum estimée jusqu'à présent
      beta  est la valeur maximum estimée jusqu'à présent
    */

evaluate_and_choose(Coul,[Succ|Succs],Conf,Depth,Alpha,Beta,Record,MeilleurSuccVal):-
    joueur_suivant(Coul,Coulp),
    alpha_beta(Coulp,Depth,Succ,Alpha,Beta,_,Valeur),
    /*writef(' Conf : %w \n Valeur : %w \n\n',[Succ,Valeur]),*/
    Valeurp is -Valeur,
    cutoff(Coul,Succ,Valeurp,Depth,Alpha,Beta,Succs,Conf,Record,MeilleurSuccVal).

evaluate_and_choose(_,[],_,_,Alpha,_,Record,(Record,Alpha)).


/* implantation alpha beta du minimax */
alpha_beta(_,0,Conf,_,_,_,Valeur) :- 
    valeur(Conf,Valeur).

alpha_beta(Coul,Depth,Conf,Alpha,Beta,Succ,Valeur) :- 
    Depth > 0,
    successeurs(Conf,Coul,Succs),
    Alpha1 is -Beta,
    Beta1 is -Alpha,
    D1 is Depth-1, 
    evaluate_and_choose(Coul,Succs,Conf,D1,Alpha1,Beta1,nil,(Succ,Valeur)).


cutoff(_,Succ,Valeur,_,_,Beta,_,_,_,(Succ,Valeur)):- 
    writef('Valeur : %w  \n Beta : %w \n\n',[Valeur,Beta]), 
    Valeur >= Beta,
    write('Cutof 1 \n'),
    !.
cutoff(Coul,Succ,Valeur,Depth,Alpha,Beta,Succs,Conf,_Record,MeilleurSuccVal):- 
    writef('Valeur : %w   Beta : %w Alpha : %w \n\n',[Valeur,Beta,Alpha]), 
    Alpha < Valeur, Valeur < Beta,
    write('Cutof 2 \n'),
    evaluate_and_choose(Coul,Succs,Conf,Depth,Valeur,Beta,Succ,MeilleurSuccVal).
cutoff(Coul,_,Valeur,Depth,Alpha,Beta,Succs,Conf,Record,MeilleurSuccVal) :- 
    Valeur =< Alpha,!, 
    evaluate_and_choose(Coul,Succs,Conf,Depth,Alpha,Beta,Record,MeilleurSuccVal).


symetrique(
    [
     [A1,A2,A3,A4,A5,A6,A7],
     [B1,B2,B3,B4,B5,B6,B7],
     [C1,C2,C3,C4,C5,C6,C7],
     [D1,D2,D3,D4,D5,D6,D7],
     [E1,E2,E3,E4,E5,E6,E7],
     [F1,F2,F3,F4,F5,F6,F7],
     [G1,G2,G3,G4,G5,G6,G7]],
    [[A7,A6,A5,A4,A3,A2,A1],
     [B7,B6,B5,B4,B3,B2,B1],
     [C7,C6,C5,C4,C3,C2,C1],
     [D7,D6,D5,D4,D3,D2,D1],
     [E7,E6,E5,E4,E3,E2,E1],
     [F7,F6,F5,F4,F3,F2,F1],
     [G7,G6,G5,G4,G3,G2,G1]]).

symetrique(
    [
     [A1,A2,A3,A4,A5],
     [B1,B2,B3,B4,B5],
     [C1,C2,C3,C4,C5],
     [D1,D2,D3,D4,D5],
     [E1,E2,E3,E4,E5]],
    [[A5,A4,A3,A2,A1],
     [B5,B4,B3,B2,B1],
     [C5,C4,C3,C2,C1],
     [D5,D4,D3,D2,D1],
     [E5,E4,E3,E2,E1]]).

symetrique(
    [
     [A1,A2,A3],
     [B1,B2,B3],
     [C1,C2,C3]],
    [[A3,A2,A1],
     [B3,B2,B1],
     [C3,C2,C1]]).

non_symetrique_de(X,Y):- not(symetrique(X,Y)).

filtre_sym([],[]).
filtre_sym([X|T],[X|R]):-
    sublist(non_symetrique_de(X),T,Ns),
    filtre_sym(Ns,R).


/*
tour(Contenu_Jeu, Coul) :-
		victoire(Coul,Contenu_Jeu).

tour(Contenu_Jeu, Coul) :-
		coup(Coul,Contenu_Jeu,Nouv_Contenu_jeu),
		victoire(Coul,Nouv_Contenu_jeu).
		

tour(Contenu_Jeu, Coul) :-
		coup(Coul,Contenu_Jeu,Nouv_Contenu_jeu),
		joueur_suivant(Coul,Coulp),
		not(victoire(Coul,Nouv_Contenu_jeu)),
		tour(Nouv_Contenu_jeu,Coulp).
*/




