I tried to write a program in Prolog to solve the well known Wolf Goat Cabbage puzzle. Given a farmer who wants to cross the river with his wolf, goat and cabbage. The boat only holds two at the same time and he cannot leave wolf with goat or goat with cabbage.
I'm aware that there are working solutions to this problem here on Stackoverflow. But I would like to find the fault in my code for learning purpose. This is my code. It results in a so called local stack overflow and I guess there is an error in the logic. Since I commented every block, it should be easy to understand.
% Helper function to check if first list
% is fully contained in second one.
subset([], []).
subset([E|Tail], [E|NTail]):-
subset(Tail, NTail).
subset([_|Tail], NTail):-
subset(Tail, NTail).
% There is space for two objects on the
% boat, but one of them must be the farmer.
crew(farmer).
crew(farmer, wolf).
crew(farmer, goat).
crew(farmer, cabbage).
% The riverside is safe if either the
% farmer is there, or not both wolf and
% goat or both goat and cabbage are there.
safe(Side) :-
member(farmer, Side).
safe(Side) :-
not(member(wolf, Side)),
not(member(goat, Side)).
safe(Side) :-
not(member(goat, Side)),
not(member(cabbage, Side)).
% To embark, objects from one riverside,
% the crew must be valid an the riverside
% must stay safe. Disembarking is easy.
embark(Crew, Side, New) :-
subset(Crew, Side),
crew(Crew),
safe(Side),
delete(Side, Crew, New).
disembark(Crew, Side, New) :-
append(Side, Crew, New).
% Crossing the river from one or the other side.
cross(Left, Right, Nextleft, Nextright) :-
embark(Left, Crew, L),
disembark(Right, Crew, R),
cross(L, R, Nextleft, Nextright).
cross(Left, Right, Nextleft, Nextright) :-
embark(Right, Crew, R),
disembark(Left, Crew, L),
cross(L, R, Nextleft, Nextright).
% Find solution to bring all objects from left
% riverside to right. Run this after consulting
% the file.
% cross([farmer, wolf, goat, cabbage], [], [], [farmer, wolf, goat, cabbage]).
What is wrong with this code? I just try to understand Prolog more in depth.
SWI-Prolog xref-based editor highlights the first problem: crew/2 is never used:
Then you maybe want this definition:
crew([farmer]).
crew([farmer, wolf]).
crew([farmer, goat]).
crew([farmer, cabbage]).
edit I think the next problem is apparent in the way you call crew/1:
embark(Crew, Side, New) :-
subset(Crew, Side),
crew(Crew),
safe(Side),
delete(Side, Crew, New).
you're passing an already formed crew instead of the one as generated by subset/2. If you enter debug mode
?- leash(-all),trace.
true.
[trace] 3 ?- cross([farmer, wolf, goat, cabbage], [], [], L).
Call: (6) stackoverflow:cross([farmer, wolf, goat, cabbage], [], [], _G1474)
...
Exit: (8) stackoverflow:subset([farmer, wolf, goat, cabbage], [farmer, wolf, goat, cabbage])
Call: (8) stackoverflow:crew([farmer, wolf, goat, cabbage])
Fail: (8) stackoverflow:crew([farmer, wolf, goat, cabbage])
Redo: (11) stackoverflow:subset([cabbage], _G1560)
...
Exit: (8) stackoverflow:subset([farmer, wolf, goat, cabbage], [farmer, wolf, goat])
Call: (8) stackoverflow:crew([farmer, wolf, goat, cabbage])
Fail: (8) stackoverflow:crew([farmer, wolf, goat, cabbage])
Redo: (10) stackoverflow:subset([goat, cabbage], _G1557)
...
that is, crew always fails...