%****************************************************************************
%*                            Devoir de PROLOG                              *
%*                  Julien Van Den Bossche / Benoît Moulin                  *
%*                cmoi@julienvdb.com / bmoulin@etu.info.unicaen.fr              *
%****************************************************************************




% ****************************************************************
% *      REPRESENTATION DE LA SCENE ET DES ACTIONS EN PROLOG     * 
% ****************************************************************

%vehicule(+Nom,+Type,+Couleur):- Un véhicule possède un Nom, un Type et une Couleur
vehicule(v1, voiture, rouge).
vehicule(v2, voiture, vert).
vehicule(m1, moto, jaune).
vehicule(m2, moto, rouge).
vehicule(c1, camion, vert).

%etat_courant(E):- permet de représenter l'état courant, E étant la file de vehicules
:-dynamic(etat_courant/1).
etat_courant([v1,vide,m1,vide,vide,v2,m2,c1,vide]).


%initialisation de la file de voitures, le file est représentée par une liste de véhicules
initialisation:- retract(etat_courant(_)), assertz(etat_courant([v1, vide, m1, vide, vide, v2, m2, c1])).


%Affichage de l'état courant
affiche([]).
affiche([vide|Liste]) :- write(vide), write(' '), affiche(Liste).
affiche([Debut|Liste]) :- vehicule(Debut, Type, Couleur), write('('), write((Debut, Type, Couleur)),  write(') '), affiche(Liste).
etat :- etat_courant(E), write('Etat courant : '), affiche(E).

%vide(+Liste) :- teste si la liste Liste ne comporte que des vides
vide([]).
vide([vide|Liste]) :- vide(Liste).

%********************************************
%*         Tests de propriétés              *
%********************************************

%vérifie une liste de propriétés dans l'état courant.
vrai(Propriete) :- etat_courant(E), vrai(E, Propriete).

%vrai(Etat,Propriété):-vérifie si une propriété est respectées dans un état.

vrai(_, []).

%teste si un véhicule est de couleur "Couleur": on regarde si le nom du véhicule existe puis si vrai alors on regarde si la couleur du véhicule match avec la couleur demandée puis si vrai alors on teste les autres propriétés.
vrai(Etat, [couleur(NomVehicule, Couleur)|L]) :- 
	member(NomVehicule, Etat),    %le véhicule doit appartenir à l'état
	vehicule(NomVehicule, _, Couleur), %On cherche un véhicule dont son nom et sa couleur matche avec NomVehicule et Couleur
	vrai(Etat, L). %on teste les autres propriétés

%teste si un véhicule est de type "Type": on regarde si le nom du véhicule existe puis si vrai alors on regarde si le type du véhicule match avec le type demandé puis si vrai alors on teste les autres propriétés.
vrai(Etat, [type(NomVehicule, Type)|Queue]) :-
	 member(NomVehicule, Etat),           %le véhicule doit appartenir à l'état
	 vehicule(NomVehicule, Type, _),      %On doit reconnaitre un véhicule dont son type matche avec Type(son nom aussi)
	 vrai(Etat, Queue).

%NomVehicule doit être dans la file puis tout ce qui est avant NomVehicule doit être vide et NomVehicule ne doit pas être vide.
vrai(Etat, [queue_de_file(NomVehicule)|Queue]) :-
	NomVehicule\=vide,
	member(NomVehicule,Etat),
	append(L,[NomVehicule|_], Etat),
	vide(L),
	vrai(Etat,Queue).


vrai(Etat, [tete_de_file(NomVehicule)|Queue]) :- 
member(NomVehicule,Etat),
	NomVehicule\=vide,
	member(NomVehicule,Etat),
	append(_, [NomVehicule|L], Etat),
	vide(L),
	vrai(Etat, Queue).

%on cherche NomVehicule1, on prend la liste après NomVehicule1, on cherche NomVehicule2 dans cette dernière: si Vehicule2 n'est précédé que de vide avant alors vrai sinon faux
vrai(Etat, [juste_derriere(NomVehicule1,NomVehicule2)|Queue]) :- 
	NomVehicule1\=vide,
	NomVehicule2\=vide,
	member(NomVehicule1,Etat),
	member(NomVehicule2,Etat),
	append(_,[NomVehicule1|L1],Etat),
	append(L2,[NomVehicule2|_],L1),
	vide(L2),
	vrai(Etat, Queue).

%après avoir trouvé NomVehicule1 on doit trouver NomVehicule2
vrai(Etat, [derriere(NomVehicule1,NomVehicule2)|Queue]) :-
	NomVehicule1\=vide,
	NomVehicule2\=vide,
	member(NomVehicule1,Etat),
	member(NomVehicule2,Etat),
	append(_,[NomVehicule1|L1],Etat),
	append(_,[NomVehicule2|_],L1),
	vrai(Etat, Queue).

vrai(Etat, [devant(NomVehicule1,NomVehicule2)|Queue]) :-
	NomVehicule1\=vide,
	NomVehicule2\=vide,
	member(NomVehicule1,Etat),
	member(NomVehicule2,Etat),
	append(_,[NomVehicule2|L1],Etat),
	append(_,[NomVehicule1|_],L1),
	vrai(Etat, Queue).

vrai(Etat, [juste_devant(NomVehicule1,NomVehicule2)|Queue]) :- 
	NomVehicule1\=vide,
 	NomVehicule2\=vide,
	member(NomVehicule1,Etat),
	member(NomVehicule2,Etat),
	append(_,[NomVehicule2|L1],Etat),
	append(L2,[NomVehicule1|_],L1),
 	vide(L2),
 	vrai(Etat, Queue).


%********************************************
%*         Les transitions                  *
%********************************************
%transition(Etat,Action,Nouvel_Etat) :- Action fait passer Etat a Nouvel_Etat. Si l'action est impossible on affichera un message d'erreur.

%permet d'avancer
transition(Etat,avance(NomVehicule),Nouvel_Etat) :- 
 %le véhicule ne doit pas être vide
 	NomVehicule\=vide,                            
 %on cherche le nom du véhicule et on prend la liste des élémnts qui le succède (L1) 
	append(Tete, [NomVehicule|L1], Etat),      
   %On regarde si L1 commence par un vide
	append([vide], Reste, L1),                    
 %si c'est le cas on concaténe la liste des éléments avant NomVehicule avec la liste contenant vide
	append(Tete, [vide], L2),                   
%puis on concaténe L2 avec la liste contenant le nom du véhicule et le reste de la file
	append(L2, [NomVehicule| Reste], Nouvel_Etat).

%Quand on est en tête de file on agrandit la file
transition(Etat,avance(NomVehicule),Nouvel_etat) :-
	NomVehicule\=vide,                              %on vérifie que véhicule n'est pas vide
	append(Tete, [NomVehicule], Etat),              %on avance dans la liste jusqu'a trouver NomVehicule
	append(Tete, [vide, NomVehicule], Nouvel_etat). %on ajoute un vide devant NomVehicule.
	

%permet de reculer
transition(Etat,recule(NomVehicule),Nouvel_etat) :-
	NomVehicule\=vide,
%On cherche NomVehicule, on prend la liste Debut des éléments avant NomVehicule puis la liste après : Reste
	append(Tete, [NomVehicule|Reste], Etat),  
%On cherche si Tete fini par un vide et on prend ce qui est avant le vide : L1  
	append(L1, [vide],  Tete),             
%On ajoute à la fin de L1 un vide, ce qui donne L2 
	append(L1, [NomVehicule], L2),   
%On concaténe L2 un vide avec la liste Reste, ce qui donne le nouvel Etat 
	append(L2, [vide|Reste], Nouvel_etat).    
	
%Dans le cas où NomVehicule est en queue de file, soit tête de liste
transition([NomVehicule|Etat],recule(NomVehicule),Nouvel_etat) :- 
	NomVehicule\=vide,
%On construit une liste avec NomVehicule, un vide puis le reste de la liste(ce qu'il y avait après NomVehicule).
	append([NomVehicule, vide], Etat,  Nouvel_etat).
	

%permet de doubler : Nom1 double Nom2
transition(Etat,double(Nom1,Nom2),Nouvel_Etat) :-
	Nom1\=vide, %Nom1 ne doit pas être vide	
	Nom2\=vide,
%on cherche Nom1, on prend la liste qui est avant Nom1(Tete) puis celle qui est après (L1)
	append(Tete, [Nom1|L1], Etat), 
%Dans L1 on cherche Nom2 en regardant si on a une place après Nom2. On prend ce qui est avant Nom2 dans L1 : L2
	append(L2, [Nom2,vide|Reste], L1), 
	vide(L2),  %L2 doit être vide 
%on concaténe le début de notre liste initiale (Tete) avec les espaces vides de L2 puis avec
	append(Tete, L2, L3), 
%L3 qui est la liste contenant la place libérée par Nom1 puis le vide devant Nom2 est remplacé par Nom1,
%ce qui donne le nouvel état.
	append(L3, [vide, Nom2, Nom1|Reste], Nouvel_Etat).

%Dans le cas où le véhicule à doubler est en début de file.	
transition(Etat,double(Nom1,Nom2),Nouvel_etat) :-  
	Nom1\=vide,
	Nom2\=vide,
%on prend la liste avant Nom1 : Tete puis la liste après Nom1 (L1) dans Etat
	append(Tete, [Nom1|L1], Etat),
%On prend la liste avant Nom2 dans L1 puis les éléments qui précédent Nom2
	append(L2, [Nom2], L1),
%L2 doit être vide
	vide(L2),
%on ajoute la Tete, la L2 ce qui donne L3	
	append(Tete, L2, L3),
%Nom1 passe devant Nom2 et Nom1 libère une place vide	
	append(L3, [vide, Nom2, Nom1], Nouvel_etat).


transition(Etat,distance_de_securite,Nouvel_etat) :- 
%Quand il n'y a rien avant le véhicule choisi
%On regarde si la queue de file commence par un vide et on récupère le reste composé d'un nom de véhicule puis d'une liste	
	append([], [Nom1|L1], Etat), 
	Nom1\=vide, 
%si le véhicule qui suit Nom1 est Nom2 alors on insère un espace entre les véhicules.
	append([Nom2], Queue, L1),
	Nom2\=vide,
 	append([Nom1, vide, Nom2], Queue, Reste),
%et on recommence avec la liste contenant les éléments suivant Nom2
	transition(Reste,distance_de_securite,Nouvel_etat).

transition(Etat,distance_de_securite,Nouvel_etat) :- 
%on cherche le premier espace vide, on crée 2 listes contenant les éléments avant et après cet espace	
	append(Tete, [vide|L1], Etat), 
%Si L1 est composé de Nom1 puis Nom2, 
	append([Nom1,Nom2], Reste, L1), 
	Nom1\=vide,
	Nom2\=vide,
%on prend l'espace vide et on le met entre Nom1 et Nom2 ->L2
	append(Tete, [Nom1,vide,Nom2], L2),
%On ajoute Reste à L2 -> Queue  
	append(L2, Reste, Queue),
%On recommence avec Reste sans revenir sur notre point de choix.
	transition(Queue,distance_de_securite,Nouvel_etat),!.
	transition(Etat,distance_de_securite,Etat).

%message d'erreur si la transition n'est pas possible.
%l'état après la transition est le même que l'état de départ .
transition(E,Action,E) :- write(Action), write(' '), write('Action Impossible'),nl,write('Veuillez saisir une autre action'),nl.


%action(+Action):- Effectue une action sur l'état courant et renvoi le nouvel etat.
action(Action) :- etat_courant(E), transition(E,Action,Nouvel_Etat), retract(etat_courant(_)), assertz(etat_courant(Nouvel_Etat)), etat.



% ****************************************************************
% *                       VISUALISATION                          * 
% ****************************************************************
:-consult('util_visu.pro').
%initialise l'état courant, ouvre une fenêtre, crée la route puis insére les véhicules dans la fenêtre
initialisationGraphique :- initialisation, ouvre_fen, cree('RN13',_), visu.

%permet de visualiser de l'état courant.
visu :-  etat_courant(E), visu_etat(E,0).

visu_etat([],_).
%si on a un vide on avance d'une place
visu_etat([vide|L],Place) :- visu_etat(L,(Place + 1)).

%Si on a un véhicule, on le crée, on le met à sa place dans la fenêtre et on va placer ce qui reste dans la liste. 
visu_etat([Nom|L],Place) :- vehicule(Nom, Type, Couleur),cree(Nom,Type,Couleur), deplace(Nom,Place), visu_etat(L,(Place + 1)).


%-------------------------------------Visualisation des actions-----------------------------------
%Dans cette partie on appelle les prédicats définis plus haut pour changer l'état courant, on efface les composants dans le fenêtre puis on remet les composants du nouvel état.
%-------------------------------------------------------------------------------------------------

visu_action(double(Nom1,Nom2)):- etat_courant(E), transition(E,double(Nom1,Nom2),Nouvel_Etat), retract(etat_courant(_)), assertz(etat_courant(Nouvel_Etat)), visu_raz,cree('RN13',_),visu.

visu_action(avance(Nom1)):- etat_courant(E), transition(E,avance(Nom1),Nouvel_Etat), retract(etat_courant(_)), assertz(etat_courant(Nouvel_Etat)), visu_raz,cree('RN13',_),visu.

visu_action(recule(Nom1)):- etat_courant(E), transition(E,recule(Nom1),Nouvel_Etat), retract(etat_courant(_)), assertz(etat_courant(Nouvel_Etat)),visu_raz,cree('RN13',_),visu.

visu_action(distance_de_securite) :- action(distance_de_securite), visu_raz,cree('RN13',_), visu.


% deboite permet de faire déboiter un véhicule.
deboite(Nom,gauche) :-
	table_ref(Nom, B,_),
	get(B,x,X),get(B,y,Y),
	dim(largmax,N),
	Y1 is Y+N,
	send(B,move,point(X,Y1)).

deboite(Nom,droite) :-
	table_ref(Nom, B,_),
	get(B,x,X),get(B,y,Y),
	dim(largmax,N),
	Y1 is (Y-N),
	send(B,move,point(X,Y1)).
