% This buffer is for notes you don't want to save.
% If you want to create a file, visit that file with C-x C-f,
% then enter the text in that file's own buffer.

:- dynamic estado/2,ambiente/1, visitados/1,objetivo/3, restricao/2,atacado/2,cont/1.

%:- initialization
%        mutex_create(environment).


visitados([[sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]).

cont(0).


incrementa_cont :-
		cont(X),
		X1 is X + 1,
		retractall(cont(_)),
		assert(cont(X1)).
/*visitados([[sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),livre(d),livre(mesa2),livre(mesa3)]).


visitados([[sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3),livre(mesa4),livre(mesa5),livre(mesa6)]]).
ambiente([sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3),livre(mesa4),livre(mesa5),livre(mesa6)]).

visitados([[sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3)]]).
ambiente([sobre(g,f),sobre(f,e),sobre(e,d),sobre(d,c),sobre(a,mesa1),sobre(b,a),sobre(c,b),livre(g),livre(mesa2),livre(mesa3)]).
*/

mesa(mesa1).
mesa(mesa2).
mesa(mesa3).

tamanho(mesa1,10000).
tamanho(mesa2,10000).
tamanho(mesa3,10000).
tamanho(a,7).
tamanho(b,6).
tamanho(c,5).
tamanho(d,4).
tamanho(e,3).
tamanho(f,2).
tamanho(g,1).
tamanho(h,0).
tamanho(i,-1).

objetivo(a,mesa3).
objetivo(b,a).
objetivo(c,b).
objetivo(d,c).
objetivo(e,d).
objetivo(f,e).
objetivo(g,f).
objetivo(h,g).
objetivo(i,h).

objetivo(user,user).

muda_estado(Agente,NovoEstado) :-
	retractall(estado(Agente,_)),
	assert(estado(Agente,NovoEstado)).
/*
encontra_objetivo_mesa(Agente,Mesa) :-
	ambiente(Ambiente),
	member(sobre(Agente,Mesa),Ambiente),
	mesa(Mesa),objetivo(Agente,Mesa),!.

encontra_objetivo_mesa(Agente,Mesa) :-
	ambiente(Ambiente),
	member(sobre(Agente,M),Ambiente),
	mesa(M),mesa(Mesa),Mesa \= M,!.
*/
encontra_objetivo_mesa(Agente,Mesa) :-
	estado(Agente,busca_fuga),
	atacado(Agente,Agressor),
	encontra_objetivo_mesa(Agressor,Restricao),
	sobre_a_mesa(Agente,M),
	mesa(Mesa), %mesa(Restricao),  % mesa(Restricao) colocada
	Mesa \= Restricao, Mesa \= M,!.

encontra_objetivo_mesa(Agente,O) :-
	objetivo(Agente,O).

/*
encontra_objetivo_mesa(Agente,Mesa) :-  % regra colocada
	atacado(Agente,Agressor),
	encontra_objetivo_mesa(Agressor,Restricao),
	\+ mesa(Restricao),
	objetivo(Agente,Mesa),!.
*/

/*
encontra_objetivo_mesa(Agente,Mesa) :-
	\+ atacado(Agente,_),
	objetivo(Agente,Mesa), 
	mesa(Mesa),!.

encontra_objetivo_mesa(Agente,Objetivo) :-
	objetivo(Agente,Objetivo).
*/

processa(Agente,satisfeito) :-
	\+ atacado(Agente,_), !.

processa(Agente,satisfeito) :-
	atacado(Agente,_), 
	muda_estado(Agente,busca_fuga).

processa(Agente,busca_fuga) :-
	retract(atacado(Agente,user)),fail.

processa(Agente,busca_fuga) :-
	atacado(Agente,Agressor),
	encontra_intrusos_fuga(Agente,Intrusos),
        encontra_objetivo_mesa(Agente,Objetivo),
	encontra_objetivo_mesa(Agressor,ObjetivoAgressor),  % encontra_objetivo_mesa
	ifthenelse(Intrusos \= [], 
		   atacar(Agente,Intrusos,Objetivo),
		   fugir(Agente,ObjetivoAgressor)),!.

processa(Agente,busca_fuga) :-
	\+ atacado(Agente,_),
	encontra_intrusos_fuga(Agente,Intrusos), Intrusos \= [],
	encontra_objetivo_mesa(Agente,Objetivo),
	atacar(Agente,Intrusos,Objetivo),!.

processa(Agente,busca_fuga) :-
	\+ atacado(Agente,_),
	encontra_intrusos_fuga(Agente,Intrusos), Intrusos = [],
	muda_estado(Agente,busca_satisfacao),!.

processa(_,busca_fuga).

processa(Agente,fuga) :-
	atacado(Agente,_),
	muda_estado(Agente, busca_fuga),!.

processa(Agente,fuga) :-
	\+ atacado(Agente,_),
	muda_estado(Agente, busca_satisfacao),!.

processa(Agente, busca_satisfacao) :-
	atacado(Agente,_),
	muda_estado(Agente, busca_fuga),!.

processa(Agente, busca_satisfacao) :-
	\+ atacado(Agente,_),
	encontra_intruso_satisfacao(Agente,Intrusos), Intrusos \= [],
	encontra_objetivo_mesa(Agente,Objetivo), % encontra_objetivo_mesa
	atacar(Agente,Intrusos,Objetivo),!.

processa(Agente, busca_satisfacao) :-
	\+ atacado(Agente,_),
	encontra_intruso_satisfacao(Agente,Intrusos), Intrusos = [],
	satisfazer(Agente),
	muda_estado(Agente, satisfeito),!.

processa(_, busca_satisfacao).

encontra_intrusos_fuga(Agente,[X]) :-
	ambiente(Ambiente),
	member(sobre(X,Agente),Ambiente),
	member(livre(X),Ambiente),!.

encontra_intrusos_fuga(Agente,[X|Intrusos]) :-
	ambiente(Ambiente),
	member(sobre(X,Agente),Ambiente),
	\+ member(livre(X),Ambiente),
	encontra_intrusos_fuga(X,Intrusos),!.
encontra_intrusos_fuga(_,[]).

encontra_intruso_satisfacao(Agente,[X]) :-
	objetivo(Agente,Objetivo),  %encontra_objetivo_mesa
	ambiente(Ambiente),
	member(sobre(X,Objetivo),Ambiente),
	X \= Agente,!.
encontra_intruso_satisfacao(_,[]).

fugir(Agente,Restricao) :-
	ifthenelse(encontra_lugar_para_fugir(Agente,Restricao,Lugar),
		   (   move_agente(Agente,Lugar), muda_estado(Agente, fuga) ),
		   true).

move_agente(Agente,Destino) :-
%	mutex_lock(environment),
	\+ atacado(Agente,Destino),
	incrementa_cont,
	cont(X),
	format(user_output,'\n~w - Movendo Agente ~w para cima de ~w',[X,Agente,Destino]),
	ambiente(Ambiente),
	\+ member(sobre(Agente,Destino),Ambiente), %o agente já está sobre o destino
	member(livre(Destino),Ambiente),
	delete(Ambiente,livre(Destino),A1),
	delete(A1,sobre(Agente,_),A2),
	member(sobre(Agente,B),Ambiente),
	append(A2,[livre(B),sobre(Agente,Destino)],L),
	retractall(ambiente(_)),
	assert(ambiente(L)),
%	retract(atacado(Agente,_)),  % retira a restrição que provocou o movimento
	retractall(atacado(_,Agente)).
%	mutex_unlock(environment).  %(Agente,_)
move_agente(_,_).

atacar(_,[],_).
atacar(Agente,[Intruso|_],_):-
%	ifthenelse(atacado(Intruso,_),retractall(atacado(Intruso,_)),true),
%	\+ atacado(Intruso,_),  % Agente
	\+ atacado(Agente,Intruso),
	estado(Intruso,satisfeito),
	assert(atacado(Intruso,Agente)),
	encontra_objetivo_mesa(Agente,Restricao),
	format(user_output,'\nAgente ~w agredindo ~w -> não ~w',[Agente,Intruso,Restricao]),!.
atacar(_,_,_).

/*
atacar(_,[],_).
atacar(Agente,[Intruso|Intrusos],_):-
	\+ atacado(Intruso,Agente),
	assert(atacado(Intruso,Agente)),
	format(user_output,'\nAgente ~w agredindo ~w',[Agente,Intruso]),
	atacar(Agente,Intrusos,_),!.
atacar(Agente,[_|Intrusos],_) :-
	atacar(Agente,Intrusos,_).
*/

encontra_lugar_para_fugir(Agente,Restricao,X) :-
	ambiente(Ambiente),
	member(livre(X),Ambiente), 
	tamanho(X,T), tamanho(Agente,T1), T >= T1,
	mesa(X),X \= Restricao,X \= Agente,
	\+ atacado(Agente,X),!.

encontra_lugar_para_fugir(Agente,Restricao,X) :-
	ambiente(Ambiente),
	member(livre(X),Ambiente),
	tamanho(X,T), tamanho(Agente,T1), T >= T1,
	X \= Restricao,X \= Agente,
	\+ atacado(Agente,X),!.
%	\+ sobre_a_mesa(X,Restricao),!.

sobre_a_mesa(Agente,Mesa) :-
	ambiente(A),
	member(sobre(Agente,Mesa),A),
	mesa(Mesa),!.

sobre_a_mesa(Agente,Mesa) :-
	ambiente(A),
	member(sobre(Agente,X),A),
	sobre_a_mesa(X,Mesa),!.

satisfazer(Agente) :-
%	\+ estado(agente,satisfeito),
	ambiente(Ambiente),
	member(livre(Agente),Ambiente),
	objetivo(Agente,Objetivo),
	\+ atacado(Agente,Objetivo),
	move_agente(Agente,Objetivo),
	retractall(atacado(_,Agente)),  % colocado
	retractall(estado(Agente,_)),
	assert(estado(Agente,satisfeito)),
	format(user_output,'\nAgente [[~w]] satisfeito\n',[Agente]),!.

ifthenelse(Condicao,Then,_) :-
	Condicao,!,Then.
ifthenelse(_,_,Else) :-
	Else.

/********************************************************
	Manipulação de Threads 
**********************************************************/

stop_threads :-
	estado(Agent,_),
	thread_send_message(Agent,message(user,Agent,true)),
	thread_join(Agent,_),fail.

restart :-
	estado(Agent,_),
	thread_exit(Agent),
	thread_join(Agent,_), fail.

create_all_threads :-
	estado(Name,_),
	\+ mesa(Name),
	ifthenelse(current_thread(Name,_),(thread_signal(Name,thread_exit(_))),true),
	fail.
create_all_threads :-
	threads,
	estado(Name,_),
	\+ mesa(Name),
	thread_create(read_message(Name),_,[alias(Name)]),fail.
create_all_threads.

read_message(Agent) :-
	repeat,
	estado(Agent,Estado),
	processa(Agent,Estado),
	fail.

snips(X) :-
	X, !.

evaluate_message(M) :-
	M.

processa_agente(Agente) :-
	estado(Agente,Estado),
	processa(Agente,Estado),!.

run :-
	repeat,
	processa_agente(a),
	processa_agente(b),
	processa_agente(c),
	processa_agente(d),
	processa_agente(e),
	processa_agente(f),
	processa_agente(g),
	processa_agente(h),
	processa_agente(i),

	ifthenelse((estado(a,satisfeito),
		    estado(b,satisfeito),
		    estado(c,satisfeito),
		    estado(d,satisfeito),
		    estado(e,satisfeito),
		    estado(f,satisfeito),
		    estado(g,satisfeito),
		    estado(h,satisfeito),
		    estado(i,satisfeito)
		   ),true,fail).

/*
:- muda_estado(mesa1,satisfeito).
:- muda_estado(mesa2,satisfeito).
:- muda_estado(mesa3,satisfeito).
:- muda_estado(d,busca_fuga).
:- muda_estado(c,busca_fuga).
:- muda_estado(b,busca_fuga).
:- muda_estado(a,busca_fuga).
*/

/*
thread_send_message(a,message(user,a,fuga(a,user,[mesa1]))).

ambiente(X),thread_send_message(a,message(user,a,tenta_satisfazer(a,X))).

*/

configura :-
	retractall(atacado(_,_)),
	retractall(ambiente(_)),
	retractall(cont(_)), assert(cont(0)),
	muda_estado(mesa1,satisfeito),
	muda_estado(mesa2,satisfeito),
	muda_estado(mesa3,satisfeito),
%	muda_estado(g,satisfeito),
%	muda_estado(f,satisfeito),
%	muda_estado(e,satisfeito),
%	muda_estado(d,satisfeito),
	muda_estado(c,satisfeito),
	muda_estado(b,satisfeito),
	muda_estado(a,satisfeito),
	assert(ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),sobre(e,d),sobre(f,e),sobre(g,f),livre(g),livre(mesa2),livre(mesa3)])).


configura_hannoi :-
	retractall(atacado(_,_)),
	retractall(ambiente(_)),
	retractall(cont(_)), assert(cont(0)),
	muda_estado(mesa1,satisfeito),
	muda_estado(mesa2,satisfeito),
	muda_estado(mesa3,satisfeito),
	muda_estado(i,satisfeito),
	muda_estado(h,satisfeito),
	muda_estado(g,satisfeito),
	muda_estado(f,satisfeito),
	muda_estado(e,satisfeito),
	muda_estado(d,satisfeito),
	muda_estado(c,satisfeito),
	muda_estado(b,satisfeito),
	muda_estado(a,satisfeito),
	assert(ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),sobre(e,d),sobre(f,e),sobre(g,f),sobre(h,g),sobre(i,h),livre(i),livre(mesa2),livre(mesa3)])).


configura_hannoi1 :-
	retractall(atacado(_,_)),
	retractall(ambiente(_)),
	retractall(cont(_)), assert(cont(0)),
	muda_estado(mesa1,satisfeito),
	muda_estado(mesa2,satisfeito),
	muda_estado(mesa3,satisfeito),
	muda_estado(e,satisfeito),
	muda_estado(d,satisfeito),
	muda_estado(c,satisfeito),
	muda_estado(b,satisfeito),
	muda_estado(a,satisfeito),
	assert(ambiente([sobre(a,mesa1),sobre(b,a),sobre(c,b),sobre(d,c),sobre(e,d),livre(e),livre(mesa2),livre(mesa3)])).


/* Para rodar:
?- configura, assert(atacado(a,user)), run.
*/







