Farmer, Wolf, Goat and Cabbage


A classic problem from Artificial Intelligence: Structures and strategies for complex problem solving by George F. Luger and William A. Stubblefield.

A farmer wishes to transfer (by boat) a wolf, a goat, and a cabbage from the left bank of a river to the right bank. If left unsupervised, the wolf will eat the goat and the goat will eat the cabbage, but nothing will happen as long as the farmer is near.
Beside the farmer there is only room for one item in the boat.

This is a way to solve the problem in Visual Prolog:

implement main
    bank = start; target.
    state = s(bank Farmer, bank Wolf, bank Goat, bank Cabbage).
    initState : state = s(start, start, start, start).
    goalState : state = s(target, target, target, target).
    cargo = alone; wolf; goat; cabbage.
    move = m(cargo Cargo, bank After).
class predicates
    solve : (state InitState) -> move* MoveList determ.
    solve(Init) = MoveList :-
        MoveList = solve1(Init, [Init]),
class predicates
    solve1 : (state S1, state* PreviousStates) -> move* MoveList nondeterm.
    solve1(goalState, _PreviousStates) = [] :- % solved
    solve1(S1, PreviousStates) = [Move | MoveList] :-
        % make one step
        Move = move_nd(S1, S2),
        % avoid illegal states
        % avoid states that we have already been in
        not(list::isMember(S2, PreviousStates)),
        % solve the rest
        MoveList = solve1(S2, [S2|PreviousStates]).
class predicates
    move_nd : (state Before, state After [out]) -> move Move multi.
    move_nd(s(A, X, Y, Z), s(B, X, Y, Z)) = m(alone, B) :- % sail alone
        B = oppositeBank(A).
    move_nd(s(A, A, X, Y), s(B, B, X, Y)) = m(wolf, B) :- % sail with wolf
        B = oppositeBank(A).
    move_nd(s(A, X, A, Y), s(B, X, B, Y)) = m(goat, B) :- % sail with goat
        B = oppositeBank(A).
    move_nd(s(A, X, Y, A), s(B, X, Y, B)) = m(cabbage, B) :- % sail with cabbage
        B = oppositeBank(A).
class predicates
    oppositeBank : (bank Bank) -> bank Opposite.
    oppositeBank(start) = target.
    oppositeBank(target) = start.
class predicates
    illegalState : (state State) determ.
    illegalState(s(A, B, B, _)) :- % wolf and goat opposite farmer
        B = oppositeBank(A),
    illegalState(s(A, _, B, B)) :- % goat and cabbage opposite farmer
        B = oppositeBank(A).
        if MoveList = solve(initState) then
            foreach m(A,B) = list::getMember_nd(MoveList) do
                stdio::writef("Sail % to %\n", A, B)
            end foreach
            stdio::write("No solution")
        end if.
end implement main