% A REGRESSION PLANNER FOR ACTIONS IN STRIPS NOTATION
% WITH LOOP DETECTION + HEURISTIC INFORMATION ON UNSATISFIABLE GOALS

% solve(G,AS,NS,P) is true if P is a plan to solve goal G that uses 
% less than NS steps.
% G is a list of atomic subgoals. AS is the list of ancestor goal lists.

solve(G,P) :-
   solve(G,[G],6,P).

solve(G,_,_,init) :-
   solved(G).

solve(G,AS,NAs,do(A,Pl)) :-
   NAs > 0,
   satisfiable(G),
   useful(G,A),
   wp(G,A,G1),
   \+ subgoal_loop(G1,AS),
   writeln(['Trying ',A,' to solve ',G]),
   writeln(['    New subgoals ',G1]),
   NA1 is NAs-1,
   solve(G1,[G1|AS],NA1,Pl).

% subgoal_loop(G,AS) is true if we are in a loop of subgoals to solve.
% This occurs if G is a more difficult to solve goal than one of its ancestors
subgoal_loop(G1,AS) :- 
   grnd(G1), member(An,AS),  subset(An,G1).

% solved(G) is true if goal list G is true initially
solved([]).
solved([G|R]) :-
   holds(G,init),
   solved(R).

% satisfiable(G) is true if (based on a priori information) it is possible for
%  goal list G to be true all at once.
satisfiable(G) :-
   \+ unsatisfiable(G).

% unsatisfiable(G) provides domain specific knowledge on when goal list G
%  is known to be impossible to satisfy.

unsatisfiable(G) :-
   member(on(X,Y),G),
   member(on(X,Z),G),
   \+ Y=Z.
unsatisfiable(G) :-
   member(holding(Y),G),
   member(holding(Z),G),
   \+ Y=Z.
unsatisfiable(G) :-
   member(on(_,Y),G),
   member(clear(Z),G),
   Y==Z.
unsatisfiable(G) :-
   member(on(_,Y),G),
   member(holding(Z),G),
   Y==Z.
unsatisfiable(G) :-
   member(on(Y,_),G),
   member(holding(Z),G),
   Y==Z.
unsatisfiable(G) :-
   member(clear(Y),G),
   member(holding(Z),G),
   Y==Z.

% useful(G,A) is true if action A is useful to solve a goal in goal list G
useful(G,A) :-
   member(S,G),
   \+ holds(S,init),
   achieves(S,A).
% domain specific rule about what may be useful to solve even if it was true
%  initially. 
useful(G,A) :-
   member(S,G),
   member(S,[handempty]), % handempty is the only such goal in this domain
   holds(S,init),
   achieves(S,A).

achieves(G,A) :-
   addlist(A,AL),
   member(G,AL).

% wp(G,A,G0) is true if G0 is the weakest precondition that needs to hold
% immediately before action A to ensure that G is true immediately after A
wp([],A,G1) :-
   preconditions(A,G1).
wp([S|R],A,G) :-
   addlist(A,AL),
   member(S,AL),
   wp(R,A,G).
wp([S|R],A,G1) :-
   addlist(A,AL),
   notin(S,AL),
   deletelist(A,DL),
   notin(S,DL),
   wp(R,A,G),
   insert(S,G,G1).

% =============================================================================

% member(X,L) is true if X is a member of list L
member(X,[X|_]).
member(X,[_|L]) :-
   member(X,L).

notin(_,[]).
notin(A,[B|C]) :-
   dif(A,B),
   notin(A,C).

% subset(L1,L2) is true if L1 is a subset of list L2
subset([],_).
subset([A|B],L) :-
   member(A,L),
   subset(B,L).

% writeln(L) is true if L is a list of items to be written on a line, followed by a newline.
writeln(L) :- \+ \+ (numbervars(L,0,_), writelnw(L) ).
writelnw([]) :- nl.
writelnw([H|T]) :- write(H), writeln(T).

% insert(E,L0,L1) inserts E into list L0 producing list L1.
% If E is already a member it is not added.
insert(A,[],[A]).
insert(A,[B|L],[A|L]) :- A==B.
insert(A,[B|L],[B|R]) :-
   \+ A == B,
   insert(A,L,R).
grnd(G) :-
   numbervars(G,0,_).
