%%% -*- prolog -*-

%%% TERM: functor(arg1,arg2)

%%% can't unify on FUNCTOR, so this is illegal:

% foo(X(a,b)) :- ...

%%% but this is okay:

% foo(X) :- ...

%%% represent "state of world" as
%%%  s(Boat, Wolf, Goat, Cabbage)
%%% where Boat, etc can be e or w.

%%% opposite sides:
flip(e, w).
flip(w, e).

%%% move(state1, move, state2).

move(s(B1,W,G,C),  nothing,  s(B2,W,G,C))  :- flip(B1,B2), legal(s(B2,W,G,C)).
move(s(B1,B1,G,C), wolf,     s(B2,B2,G,C)) :- flip(B1,B2), legal(s(B2,B2,G,C)).
move(s(B1,W,B1,C), goat,     s(B2,W,B2,C)) :- flip(B1,B2), legal(s(B2,W,B2,C)).
move(s(B1,W,G,B1), cabbage,  s(B2,W,G,B2)) :- flip(B1,B2), legal(s(B2,W,G,B2)).

%%% legal(state), means state is legal
legal(S) :- safe(goat,S), safe(cabbage,S).

%%% legal/2(criterion, state), means state satisfies that criterion

%%% double solution (yuck)
%safe(goat, s(B,_,B,_)).
%safe(goat, s(_,W,G,_)) :- flip(W,G).

safe(goat, s(B,_,B,_)).
safe(goat, s(W,W,G,_)) :- flip(W,G).

safe(cabbage, s(B,_,_,B)).
safe(cabbage, s(G,_,G,C)) :- flip(G,C).

%%% find a solution without explicit list of moves.

% ?- move(s(e,e,e,e), M1, S1), move(S1, M2, S2), move(S2, M3, S3), move(S3, M4, S4), move(S4, M5, S5), move(S5, M6, S6), move(S6, M7, s(w,w,w,w)).

%%% list of moves

moves(S, [], S).
moves(S1, [M|Ms], Sn) :- move(S1, M, S2), moves(S2, Ms, Sn).

%%% find a solution, this goes off into never-never land
% -? moves(s(e,e,e,e), Ms, s(w,w,w,w)).

%%% but this searches "breath first", gradually trying longer & longer lists
% ?- append(Ms,[],Ms), moves(s(e,e,e,e), Ms, s(w,w,w,w)).



%%% could have written:
%flip(e,w) ; flip(w,e).

% foo(X) :- bar(a,X), baz(b,X).  %%% AND - ,
% foo(X) :- bar(a,X); baz(b,X).  %%% OR  - ;

% %%% shorthand for:
% foo(X) :- bar(a,X).
% foo(X) :- baz(b,X).

% %%% more complex example:
% foo(..) :- bar(..), baz(...), (zonk(...); gronk()), zip(...).
