:- ['5_flux'].
:- ['5_wumpus_simulator'].

init(Z0) :- Z0 = [has(arrow),wumpus(WX,WY),alive|Z],
	    [WX,WY] :: [1..5],
            not_holds(wumpus(1,1),Z0),
            not_holds_all(wumpus(_,_),Z),
            not_holds(pit(1,1),Z),
            not_holds_all(pit(_,0),Z),
            not_holds_all(pit(_,6),Z),
            not_holds_all(pit(0,_),Z),
            not_holds_all(pit(6,_),Z),
            not_holds_all(at(_,_),Z),
            not_holds_all(facing(_),Z),
            duplicate_free(Z0).

main :- init(Z0), execute(enter, Z0, Z1),
        Cpts = [[1,2]], Btr = [], Visited = [(1,1)],
        main_loop(Cpts, Btr, Visited, Z1).

main_loop([Dirs|Cpts], Btr, Vis, Z0) :-
   write('Cpts:'), writeln([Dirs|Cpts]),
   write('Btr:'), writeln(Btr),
   write('Vis:'), writeln(Vis),
   writeln("------------------"),
   holds(at(X1, Y1), Z0), try_wumpus_hunt(X1,Y1,Z0,Z1),
   (knows(gold(X1,Y1),Z1) -> execute(grab,Z1,_);
    Dirs = [Dir|RemDirs] -> 
       (explore(X1, Y1, Dir, Vis, Z1, Z2) -> 
	   holds(at(X2,Y2),Z2), main_loop([[1,2,3,4],RemDirs|Cpts],[Dir|Btr],[(X2,Y2)|Vis],Z2);
	   main_loop([RemDirs|Cpts],Btr,Vis,Z1)
       );
    backtrack(Cpts, Btr, Vis, Z1)
   ).

try_wumpus_hunt(X,Y,Z1,Z2) :-  knows(alive,Z1), knows(has(arrow),Z1), 
   knows_val([Xw,Yw], wumpus(Xw,Yw),Z1), in_direction(X,Y,D,Xw,Yw) ->
      turn_to(D,Z1,Z), execute(shoot,Z,Z2);
      Z1 = Z2.

explore(X,Y,D,Vis,Z1,Z2) :-
   adjacent(X,Y,D,X1,Y1), \+member((X1,Y1),Vis), knows_not(pit(X1,Y1),Z1),
   (knows_not(wumpus(X1,Y1),Z1);knows_not(alive,Z1)),
   turn_to(D,Z1,Z), execute(go,Z,Z2).

turn_to(D,Z1,Z2) :-
   knows(facing(D),Z1) -> Z2=Z1;
   execute(turn,Z1,Z), turn_to(D,Z,Z2).

backtrack(_,[],_, _).
backtrack(Cpts,[Dir|Btr],Vis,Z) :-
   Reverse is (Dir + 1) mod 4 + 1,
   turn_to(Reverse, Z, Z1), execute(go,Z1,Z2),
   main_loop(Cpts,Btr,Vis,Z2).

%%%%%%%% auxiliary %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 adjacent(X,Y,D,X1,Y1) :-
   [X,Y,X1,Y1]::1..5, D::1..4,
       (D#=1) #/\ (X1#=X)   #/\ (Y1#=Y+1) % north
   #\/ (D#=3) #/\ (X1#=X)   #/\ (Y1#=Y-1) % south
   #\/ (D#=2) #/\ (X1#=X+1) #/\ (Y1#=Y)   % east
   #\/ (D#=4) #/\ (X1#=X-1) #/\ (Y1#=Y).  % west

 in_direction(X,Y,D,X1,Y1) :-
   [X,Y,X1,Y1]::1..5, D::1..4,
       (D#=1) #/\ (X1#=X) #/\ (Y1#>Y)  % north
   #\/ (D#=3) #/\ (X1#=X) #/\ (Y1#<Y)  % south
   #\/ (D#=2) #/\ (X1#>X) #/\ (Y1#=Y)  % east
   #\/ (D#=4) #/\ (X1#<X) #/\ (Y1#=Y). % west

%%%%%%%% state update %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 state_update(Z1,enter,Z2,[B,S,G]) :-
   update(Z1,[at(1,1),facing(1)],[],Z2),
   breeze_perception(1,1,B,Z2),
   stench_perception(1,1,S,Z2),
   glitter_perception(1,1,G,Z2).

 state_update(Z1,exit,Z2,[]) :-
   holds(facing(D),Z1),
   update(Z1,[],[at(1,1),facing(D)],Z2).

 state_update(Z1,turn,Z2,[]) :-
   holds(facing(D),Z1),
   (D#<4 #/\ D1#=D+1) #\/ (D#=4 #/\ D1#=1),
   update(Z1,[facing(D1)],[facing(D)],Z2).

 state_update(Z1,grab,Z2,[]) :-
   holds(at(X,Y),Z1),
   update(Z1,[has(gold)],[gold(X,Y)],Z2).

 state_update(Z1,shoot,Z2,[S]) :-
   ( S=true, update(Z1,[],[has(arrow),alive],Z2)
     ; S=false, update(Z1,[],[has(arrow)],Z2) ).

 state_update(Z1,go,Z2,[B,S,G]) :-
   holds(at(X,Y),Z1), holds(facing(D),Z1),
   adjacent(X,Y,D,X1,Y1),
   update(Z1,[at(X1,Y1)],[at(X,Y)],Z2),
   breeze_perception(X1,Y1,B,Z2),
   stench_perception(X1,Y1,S,Z2),
   glitter_perception(X1,Y1,G,Z2).

%%%%%%%% percepts %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 stench_perception(X,Y,Percept,Z) :-
   XE#=X+1, XW#=X-1, YN#=Y+1, YS#=Y-1,
   ( Percept=false, not_holds(wumpus(XE,Y),Z),
                    not_holds(wumpus(XW,Y),Z),
                    not_holds(wumpus(X,YN),Z),
                    not_holds(wumpus(X,YS),Z) ;
     Percept=true,
       or_holds([wumpus(XE,Y),wumpus(X,YN),
           wumpus(XW,Y),wumpus(X,YS)],Z) ).

 breeze_perception(X,Y,Percept,Z) :-
   XE#=X+1, XW#=X-1, YN#=Y+1, YS#=Y-1,
   ( Percept=false, not_holds(pit(XE,Y),Z),
                    not_holds(pit(XW,Y),Z),
                    not_holds(pit(X,YN),Z),
                    not_holds(pit(X,YS),Z) ;
     Percept=true,
       or_holds([pit(XE,Y),pit(X,YN),
           pit(XW,Y),pit(X,YS)],Z) ).

 glitter_perception(X,Y,Percept,Z) :-
   Percept=false, not_holds(gold(X,Y),Z) ;
   Percept=true,  holds(gold(X,Y),Z).
