/*

 Jmeno Autora: VOJTECH HLAVES
 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! Editovany Martou Vomlelovou
 Skupina: INF 33, 2. roc.
 
 Nazev programu: Wumpus

 Prolog: SWI Prolog ver 5.0.10

 Zadani: Je dana jeskyne (matice MxN), vstup jeskyne je na pozici 1,1. V jeskyni se mohou
         vyskytovat tyto prvky: nejvyse 1x Zlato, nejvyse 1x Wumpus, libovolne x Diry.
         Na jednom policku muze byt pouze dvojice Zlato a Wumpus.
         Pocitac resi agenta (na zacatku behu programu je na souradnicich vstupu do jeskyne,
         s timze o jeskyni nevi zhola nic),
         ktery ma, pokud mozno, projit jeskyni vzit zlato a odejit s nim z jeskyne. Agent
         umi jit Vpred, zahnout Vlevo, zahnout Vpravo, Vzit zlato, Polozit zlato, Vystrelit.
         Pri vstupu na Diru nebo Wumpuse je agent mrtev. Agent umi poznat okoli pomoci
         predikatu vjem, ktere zjisti, zdali je na okolnich polickach (okolni policko
         je vzdalene o 1 ve vertikalnim xor 1 v horizontalnim smeru) Dira ci Wumpus, nebo
         zdali Agent stoji na Zlate. Dale ma Agent 1 sip, ktery muze kdykoliv Vystrelit 
         ve smeru, kterym je otocen. Doleti-li sip na policko s Wumpusem je Wumpus mrtev, jiz
         lze na nej bez postihu slapnout a uz nesmrdi.

 Spusteni: Uzivatelem ovladany agent: go/0.

 Vstupni data: Matice reprezentujici jeskyni (DemodataX /1, viz komentar u kazde jeskyne).
 Vystupni data: V jakem se nachazi agent stavu (Stav - [Zije,Nezije]x[Prinesel zlato, Neprinesl])
                (v prubehu programu se mohou tisknou podrobne informace, viz Mozne upravy)

 Datove struktury: Matice MxN, Ukladani faktu do DB (fakt - info, zda drzi agent zlato)

 Algoritmy: *) Prace se seznamy (spojovani,otaceni). Reseni: trivialni
            *) Prace s matici (hledani prvku, zjistovani prvku na [X,Y], zjistovani vjemu). 
               Rreseni: postupne trhani hlav vnejsiho seznamu, pote trhani hlav vnitrniho
             *) Uzivatelske rozhrani - rekurzivne volani user/9 dokud Agent zije a je v jeskyni.
               V pripade nesmyslneho (ale syntakticky spravneho) prikazu, je tento prikaz
               ignorovan.


  Program jiste funguje kdyz: *) V bludisti je libovolny pocet zlata (tj i 0)
                             *) V bludisti je libovolny pocet der
                             *) V bludisti je nejvyse 1 Wumpus
                             *) Agent ma libovolny pocet sipu (tj i 0)
                             *) Na pozicich 1,1 1,2 a 2,1 je volno (jinak by se nedala jeskyne
                                vubec prochazet)

 Pouzite preddefinovane predikaty: setof/3, asserta/1, retract/1, number/1

*/

% Prvky jeskyne:
% 0 - OK
% D - Dira
% Z - Zlato
% W - Wumpus
% & - Wumpus se zlatem

% Smer agenta
% 0 - Zapad, 1 - Jih, 2 - Vychod, 3 - Sever


go :- demodata8(S),preuser(S),user(S,1,1,2,1,Vysledek,Xend,Yend,Otocend),vysledekuser(Vysledek,Xend,Yend,Otocend).

% Zobrazovani dalsich informaci pri behu programu
% v pripade ze ovlada Agenta uzivatel
io_jeskyne_user(0).  % vypisovani jeskyne, kdyz ji prochazi uzivatel  - vypis po provedeni seznamu akci
io_vypis_otoceni(0). % vypisuje smer na ktery je Agent otocen po vykonani akci. Toto neni trba,
                     % kdyz se vypisuje jeskyne


% Jeskyne

% Agent po projiti cele leve casti zabije Wumpa a vezme z podnej zlato a odejde
demodata1([[ 0 , 0 , 0 ,'D', 0 ,0,'D'],
           [ 0 , 0 , 0 ,'D','D',0,'D'],
           [ 0 , 0 , 0 , 0 , 0 ,0,'D'],
           ['D', 0 , 0 , 0 ,'&',0, 0 ],
           [ 0 , 0 , 0 , 0 , 0 ,0, 0 ],
           [ 0 , 0 , 0 ,'D','D',0, 0 ]]).


demodata8([[ 0 , 0 ,'D'],
           [ 0 ,'&','D'],
           [ 0 ,'D', 0 ]]).


predmetyvjeskyni(['D','Z','W','&']).


/************ HLEDANI PRVKU NA X,Y */

%pole (+struktura, +Xova souradnice, +Yova souradnice, -Co)
%vrati prvek z matice na souradnici X,Y
pole(D,X,Y,Z) :- Y1 is Y-1,pole2(D,X,Y1,Z).
pole2([_|D],X,Y,Z) :- Y>0,Y1 is Y-1,pole2(D,X,Y1,Z).
pole2([A|_],X,0,Z) :- X1 is X-1,prvek(A,X1,Z).
%prvek(+Seznam,+Kolikaty,-Vysledek)
%najde X-ty prvek seznamu
prvek([_|A],X,Z) :- X>0,X1 is X-1,prvek(A,X1,Z).
prvek([Z|_],0,Z).


/************ HLEDANI PRVKU V MATICI, JEJICH HODNOTA JE */

% hledej(+Matice,-X,-Y,+Co).
% najde vyskyt Co v Matici
hledej(S,X,Y,Hodnota) :- hledejprvky(S,X,1,Y,Hodnota).
hledejprvky([H|T],X,Y,YY,Hodnota) :- (prohledejpole(H,1,X,Hodnota),YY=Y);
                                     (Y1 is Y+1, hledejprvky(T,X,Y1,YY,Hodnota)).

% prohledejpole(+Pole,+Start,-Kde,+Co)
% urci souradnici prvku Co v poli start, kdy prvni policko ma hodnotu Start
prohledejpole([Co|_],X,X,Co).
prohledejpole([_|T],X,XX,Co) :-  X1 is X+1,prohledejpole(T,X1,XX,Co).


/************ NAHRAZENI PRVKU V MATICI */

% nahrad(+struktura,+X souradnice,+Y souradnice,+Cim,-struktura zmenena)
nahrad(D,X,Y,Cim,Z) :- X1 is X-1,Y1 is Y-1,(nahrad2(D,X1,Y1,Cim,Z)). % ;Z=D

% nahrad2(+struktura,+X souradnice,+Y souradnice,+Cim,-struktura zmenena),
% jen uz budou zmenseny indexy - aby 1,1 byl horni levy roh
nahrad2([H|T],X,Y,Cim,[H|Z]) :- Y>0,Y1 is Y-1,nahrad2(T,X,Y1,Cim,Z).
nahrad2([H|T],X,0,Cim,Z) :- nahrad3(H,X,Cim,Z1),Z=[Z1|T].

%nahrad3(+Seznam,+Kolikaty,-Vysledek)
%nahradi X-ty prvek seznamu
nahrad3([H|A],X,Cim,[H|Z]) :- X>0,X1 is X-1,nahrad3(A,X1,Cim,Z).
nahrad3([_|T],0,Cim,[Cim|T]).


/************ VJEM */

%vjem(+struktura,+X,+Y,-VjemTahne,-VjemSmrdi,-VjemZlato). % Vjem=1 Citim, Vjem=0 OK
vjem(D,X,Y,ZT,ZS,ZZ) :- ((((X1 is X-1,pole(D,X1,Y,Z1),Z1='D');
                          (X2 is X+1,pole(D,X2,Y,Z1),Z1='D');
                          (Y1 is Y-1,pole(D,X,Y1,Z1),Z1='D');
                          (Y2 is Y+1,pole(D,X,Y2,Z1),Z1='D')),ZT=1);ZT=0),

                          ((((X3 is X-1,pole(D,X3,Y,Z2),(Z2='W';Z2='&'));
                          (X4 is X+1,pole(D,X4,Y,Z2),(Z2='W';Z2='&'));
                          (Y3 is Y-1,pole(D,X,Y3,Z2),(Z2='W';Z2='&'));
                          (Y4 is Y+1,pole(D,X,Y4,Z2),(Z2='W';Z2='&'))),ZS=1);ZS=0),

                          ((pole(D,X,Y,Z3),Z3='Z',ZZ=1);ZZ=0),!.


/************ VYPSANI VJEMU */

%vypisvjem(+Tahne,+Smrdi,+Zlato).
vypisvjem(0,0,0) :- writeln('Okoli vypada bezpecne'),!.
vypisvjem(ZT,ZS,ZZ) :- ((ZT=1,writeln('Tahne to tu')),!;true),
                       ((ZS=1,writeln('Smrdi to tu')),!;true),
                       ((ZZ=1,writeln('Je tu zlato')),!;true).


/************ VYKRESLENI JESKYNE */

% jeskyne(+struktura, +X agenta, +Y agenta)
% zmeni policko s agentem na A, pokud je zadan agent mimo jeskyni neni nakreslen
jeskyne(D,X,Y,Otoc) :- zobacek(Otoc,Zob),nahrad(D,X,Y,Zob,D1),jeskyne2(D1),writeln('').
% vykresli jeskyni
% jeskyne2(+struktura)
jeskyne2([H|T]) :- writeln(H),jeskyne2(T).
jeskyne2([]).

% zobacek(?Otoceni,?Graficke otoceni) - vybere pohled agenta, podle jeho otoceni
zobacek(-1,0). % nechci vypisovat
zobacek(0,'<').
zobacek(1,'v').
zobacek(2,'>').
zobacek(3,'^').
zobacek(8,'<').
zobacek(9,'v').
zobacek(10,'>').
zobacek(11,'^').


/************ POHYB */

% pouze testovaci
% ag(X1,Y1,Otoceni,X2,Y2) :- prazdnamatice(X),jeskyne(X,X1,Y1,Otoceni),
%                            agent_vpred(X,X1,Y1,Otoceni,X2,Y2),jeskyne(X,X2,Y2,Otoceni).

% agent_zvedni/poloz(+Jeskyne,+X,+Y,-Nova jeskyne)
% zvedne resp. polozi zlato, je-li mozno
agent_zvedni(S,X,Y,S1) :- (ag_gold(0),pole(S,X,Y,'Z'),writeln('Agent zveda zlato'),nahrad(S,X,Y,0,S1),retract(ag_gold(0)),asserta(ag_gold(1)),!);true.
agent_poloz(S,X,Y,S1) :- (ag_gold(1),writeln('Agent poklada zlato'),nahrad(S,X,Y,'Z',S1),retract(ag_gold(1)),asserta(ag_gold(0)),!);true.

% agent_zvedniuser/polozuser zbira ci poklada zlato
agent_zvedniuser(S,X,Y,S1) :- pole(S,X,Y,'Z'),writeln('Agent zveda zlato'),nahrad(S,X,Y,0,S1),retract(ag_gold(0)),asserta(ag_gold(1)).
agent_polozuser(S,X,Y,S1) :- writeln('Agent poklada zlato'),nahrad(S,X,Y,'Z',S1),retract(ag_gold(1)),asserta(ag_gold(0)).


% agent vpred(+Jeskyne,+Puvod.X,+Puvod.Y,+Smer otoceni,)
% posune, pokud je to mozne o 1 policko ve smeru jakym je otoceny
agent_vpred(S,X1,Y1,Otoceni,Otoceni2,X2,Y2) :- pluspodlesmeru(Otoceni,X3,Y3),
                                      ( (X2 is X1+X3,Y2 is Y1+Y3,pole(S,X2,Y2,Co),
									   zpracuj(Co,Otoceni,Otoceni2),
                                      writeln('Agent jde vpred'),!);
                                      (X2=X1,Y2=Y1,Otoceni2=Otoceni), writeln('Agent narazil na zed')).
% zmeneni Otoceni2=-1 by se mely stat "horici zdi", kterych se agent nesmi dotknout									                                      



% agent_ven(+Otoceni,-Otoceni) necha agenta odejit
agent_ven(_,Otoceni2) :- Otoceni2 is -2.

%agent_vlevo/vpravo(+Otoceni1,-Otoceni2) otoci agenta od +-90 stupnu
agent_vlevo(Otoceni,Otoceni2) :- writeln('Agent se toci do leva'),
                                 Otoceni3 is Otoceni+1,mod4(Otoceni3,Otoceni2).
agent_vpravo(Otoceni,Otoceni2) :- writeln('Agent se toci do prava'),
                                  Otoceni3 is Otoceni-1,mod4(Otoceni3,Otoceni2).
%pouze testovatci
%sss(X,Y,O1,C) :- demodata(S),jeskyne(S,X,Y,O1),agent_vystrel(S,X,Y,O1,C).

sip(S,X,Y,O1,C) :- (hledej(S,Xw,Yw,'W'),!;(hledej(S,Xw,Yw,'&'))),Xx is Xw-X,Yy is Yw-Y,pluspodlesmeru(O1,Xs,Ys),
                   ((Xs=:=0,Xx=:=0,Yyy is Yy/Ys,Yyy>0,C is 8,writeln('Wumpus je mrtev!'),!);
				   (Ys=:=0,Yy=:=0,Xxx is Xx/Xs,Xxx>0,C is 8,writeln('Wumpus je mrtev!'),!);
				   C is 0).

% agent_vystrel(_,_,_,O1,O2) :- S1<1,true,O2=O1,!.
% agent_vystrel(+Jeskyne,+X,+Y,+Otoceni,-Otoceni,) - vystreli sip
agent_vystrel(S,X,Y,O1,O2) :- writeln('Agent strili'),sip(S,X,Y,O1,C),O2 is O1+C. 

% agent_vystreluser(+Jeskyne,+X,+Y,+Otoceni,-Otoceni,-Struktura) - vystreli sip, pro uziv.mod
agent_vystreluser(S,X,Y,O1,O1,S1) :- writeln('Agent strili'),sip(S,X,Y,O1,C),((C=8,((hledej(S,Xw,Yw,'W'),
                                     nahrad(S,Xw,Yw,0,S1),!);(hledej(S,Xw,Yw,'&'),nahrad(S,Xw,Yw,'Z',S1))),! );(S1=S)). 


% pluspodlesmeru(+Smer,-X,-Y)
% vraci relativni pohyb ve smeru Smer
pluspodlesmeru(0,X,Y) :- X is -1,Y is 0.
pluspodlesmeru(1,X,Y) :- X is 0,Y is 1.
pluspodlesmeru(2,X,Y) :- X is 1,Y is 0.
pluspodlesmeru(3,X,Y) :- X is 0,Y is -1.

% v pripade uspesneho utoku na Wumupa
pluspodlesmeru(8,X,Y) :- X is -1,Y is 0.
pluspodlesmeru(9,X,Y) :- X is 0,Y is 1.
pluspodlesmeru(10,X,Y) :- X is 1,Y is 0.
pluspodlesmeru(11,X,Y) :- X is 0,Y is -1.


/************ PROVADENI AKCI */



% delejakceuser(+Jeskyne,+X,+Y,+Otoceni,+Sipy,-X,-Y,-Otoceni koncove,-Sipy,+Seznam akci,-Jeskyne)
% dostaneli na vstupu seznam akci {ven,vlevo,vpravo,vpred,vystrel}
% provede je, stimze ohlida, zdali agent prezil ci nikoliv - pro uzivatelsky mod

delejakceuser(S,X,Y,Otoceni,Sipy,X,Y,Otoceni,Sipy,[],S) :- !.
delejakceuser(S,X,Y,-1,Sipy,X,Y,-1,Sipy,_,S) :- !.
delejakceuser(S,X,Y,Otoceni,Sipy,Xx,Yy,Otocenix,Sipyy,[H|T],Sx) :- 
                                  proveduser(S,X,Y,Otoceni,Sipy,H,X1,Y1,Otoceni2,Sipy1,S1),								  
                                  delejakceuser(S1,X1,Y1,Otoceni2,Sipy1,Xx,Yy,Otocenix,Sipyy,T,Sx).

% proveduser(+Jeskyne, +Xpocatecni,+Ypocatecni,+Otacenipuv.,+Pocet sipu,+Akce,-Xkoncove,-Ykoncove,-Ot.konc,-Sipu po akci,-Struktura)
% provede libovolny prikaz
proveduser(S,1,1,Otoceni1,Sip1,ven,1,1,Otoceni2,Sip1,S) :- agent_ven(Otoceni1,Otoceni2),!.
proveduser(S,X,Y,Otoceni1,Sip1,vlevo,X,Y,Otoceni2,Sip1,S) :- agent_vlevo(Otoceni1,Otoceni2),!.
proveduser(S,X,Y,Otoceni1,Sip1,vpravo,X,Y,Otoceni2,Sip1,S) :- agent_vpravo(Otoceni1,Otoceni2),!.
proveduser(S,X,Y,Otoceni1,Sip1,vpred,X1,Y1,Otoceni2,Sip1,S) :- agent_vpred(S,X,Y,Otoceni1,Otoceni2,X1,Y1),!.
proveduser(S,X,Y,Otoceni1,Sip1,vystrel,X,Y,Otoceni2,Sip2,S1) :- Sip1>0,agent_vystreluser(S,X,Y,Otoceni1,Otoceni2,S1),
                                                                Sip2 is Sip1 - 1,!.

proveduser(S,X,Y,Otoceni1,Sip1,zvedni,X,Y,Otoceni1,Sip1,S1) :- pole(S,X,Y,'Z'),agent_zvedniuser(S,X,Y,S1),!.
proveduser(S,X,Y,Otoceni1,Sip1,poloz,X,Y,Otoceni1,Sip1,S1) :- ag_gold(1),agent_polozuser(S,X,Y,S1),!.
proveduser(S,X,Y,_,Sip1,zemri,X,Y,-1,Sip1,S) :- !.
proveduser(S,X,Y,Otoceni1,Sip1,_,X,Y,Otoceni1,Sip1,S).
                                                           

/************ VYHODNOCAVANI, ZDA-LI AGENT ZIJE */

% zjisti zdali agent postoupil do bezpeci ci nikoliv
% zpracuj(+Kam slapl,+Puvodni otoceni,-Budouci otoceni)
zpracuj(Co,Otoc1,Otoc2) :- Co=0,Otoc2=Otoc1,!.
zpracuj(Co,Otoc1,Otoc2) :- Co='Z',Otoc2=Otoc1,!.
zpracuj(Co,Otoc1,Otoc2) :- Co='W',Otoc1>7,Otoc2=Otoc1,!.
zpracuj(Co,Otoc1,Otoc2) :- Co='&',Otoc1>7,Otoc2=Otoc1,!.

% jiny pripad je smrt
zpracuj(_,_,Otoc2) :- Otoc2 is -1.


/************ SPOJENI SEZNAMU */

% spoj(?Co,? S cim, ?Vysledek)
% klasicke spojeni
spoj([],S,S).
spoj([H|S1],S2,[H|S3]) :- spoj(S1,S2,S3).

% spojeni, ve kterem je kazdy prvek jen 1x
% spojex(+Co,+S cim,-Vysledek)
spojex([],S,S).
spojex([H|S1],S2,S3) :- prvek(H,S2),spojex(S1,S2,S3),!.
spojex([H|S1],S2,[H|S3]) :- spojex(S1,S2,S3),!.


/************ PRVKEM SEZNAMU */

% prvek(?Co,+Ceho)
prvek(X,[X|_]).
prvek(X,[_|T]) :- prvek(X,T).


/************ SKUBANI HLAVY */

% uskubnihlavu(+Cemu, -Zbytek)
uskubnihlavu([_|C],C).


/************ OTACENI SEZNAMU */

% otoc1(+Co, +S cim,Co obracene pripojene za S cim)
otoc1([],L,L).
otoc1([X|T],L,P) :- otoc1(T,[X|L],P).

% otoc(+X,-Y)
% otoci seznam
otoc(X,Y) :- otoc1(X,[],Y).


/************ PRACE S DATABAZI */


% dbclear - odstrani vsechny predchozi zaznamy do DB
dbclear :-    retractall(ag_gold(_)),asserta(ag_gold(0)).


/************ MATEMATICKE FCE */

% mod4(+X,-Y)
% z duvodu setreni pracuje jen na omezenem intevralu
mod4(X,Y) :- ((X>3,Y is X-4),!;(X<0,Y is X+4)),!;Y=X.


/************ PRE - POST */


% vysledekuser(+Vysledek,+X,+Y,+Otoceni) - vytiskne vysledek pro uzivatelsky mod
vysledekuser(X,Xend,Yend,Otocend) :-
                                 ((Otocend is -1, write('Agent: '),writeln(X),write('Na souradnicich x:'),write(Xend),write(' y:'),writeln(Yend),!);
								 (write('Agent: '),writeln(X))),

					   ((ag_gold(1),Otocend is -2, writeln('Zlato: Prinesl'),
					      writeln('Cil splnen'),!);
                                 (((Otocend is -1,writeln('Cil nesplnen'),!);(writeln('Zlato: Nerinesl'),writeln('Cil nesplnen')))  )).



% preuser(+jeskyne) - provede inicializaci
preuser(_) :- dbclear,writeln('Agent vesel do jeskyne a je otocen na zapad.').


/************ USER */

vypis_otoceni(0) :- writeln('Agent je otocen na vychod').
vypis_otoceni(1) :- writeln('Agent je otocen na jih').
vypis_otoceni(2) :- writeln('Agent je otocen na zapad').
vypis_otoceni(3) :- writeln('Agent je otocen na sever').

% user(+Jeskyne,+X,+Y,+Otoceni,-Vysl,-X,-Y,-Otoceni)
% Agent pro pohyby z klavesnice
% Pri volani nesmyslneho pokynu, je tento pokyn ignorovan
 
user(_,X,Y,-2,_,Vysl,X,Y,-2) :- Vysl='Opustil zdrav jeskyni',!.
user(_,X,Y,-1,_,Vysl,X,Y,-1) :- Vysl='Zemrel',!.
user(S,X,Y,Otoc,Sip,Vysl,Xend,Yend,Otocend) :-
                      vjem(S,X,Y,ZT,ZS,ZZ),vypisvjem(ZT,ZS,ZZ),
                      ((io_jeskyne_user(1),jeskyne(S,X,Y,Otoc),!);(true)),
                      ((io_vypis_otoceni(1),vypis_otoceni(Otoc),!);(true)),
			    writeln('Pokyny: vpred,vlevo,vpravo,zveni,poloz,vystrel,zemri,ven'),
			    write('Zadej seznam pokynu: '),read(A),
		          delejakceuser(S,X,Y,Otoc,Sip,X1,Y1,Otoc1,Sip1,A,S1),
			  write('agent je na policku '),write(X1),write(':'),writeln(Y1),
                      user(S1,X1,Y1,Otoc1,Sip1,Vysl,Xend,Yend,Otocend).


