:- ['2_special_flux'].
:- ['perform1'].

init(Z) :- Z = [at(2), request(2, muffin, 4), request(2, coffee, 3), request(1, tea, 4)].

main :- init(Z), main_loop(Z).

main_loop(Z) :-
	poss(drop(X), Z)   -> execute(drop(X), Z, Z1), main_loop(Z1);
	poss(pickup(X), Z) -> execute(pickup(X), Z, Z1), main_loop(Z1);
	wrong_dir(Z)       -> execute(turn, Z, Z1), main_loop(Z1);
	req_left(Z)        -> execute(go, Z, Z1), main_loop(Z1);
	true.


poss(drop(O), Z) :- holds(request(_, O, Y), Z), holds(at(Y), Z), holds(carries(O), Z).

poss(pickup(O), Z) :- holds(request(X, O, Y), Z), holds(at(X), Z),
                      \+ (holds(carries(_), Z); holds(request(X, _, Yp), Z), closer(Yp, Y, X)).

wrong_dir(Z) :- closest(R, R1, Z),
               (R < R1, \+ holds(facing_north, Z);
                R > R1, holds(facing_north, Z)).

req_left(Z) :- holds(request(_, _, _), Z).


closest(R, R1, Z) :- holds(at(R), Z),
                     (holds(carries(O), Z), holds(request(_, O, R1), Z),
			\+ (holds(request(_, O, R2), Z), closer(R2, R1, R))
                     ;
                     \+ holds(carries(_), Z), holds(request(R1, _, _), Z),
			\+ (holds(request(R2, _, _), Z), closer(R2, R1, R))).




closer(R1, R2, R) :- abs(R1 - R) < abs(R2 - R).


state_update(Z1, drop(O), Z2) :- holds(at(X), Z1), update(Z1, [], [request(_, O, X), carries(O)], Z2).

state_update(Z1, pickup(O), Z2) :- update(Z1, [carries(O)], [], Z2).

state_update(Z1, turn, Z2) :- holds(facing_north, Z1), update(Z1, [], [facing_north], Z2);
                              \+ holds(facing_north, Z1), update(Z1, [facing_north], [], Z2).

state_update(Z1, go, Z2) :- holds(at(X), Z1),
                              (holds(facing_north, Z1), update(Z1, [at(X1)], [at(X)], Z2), X1 is X+1;
                              \+ holds(facing_north, Z1), update(Z1, [at(X1)], [at(X)], Z2), X1 is X-1).
