Farmer, Wolf, Goat and Cabbage

From wiki.visual-prolog.com

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
 
domains
    bank = start; target.
    state = s(bank Farmer, bank Wolf, bank Goat, bank Cabbage).
 
constants
    initState : state = s(start, start, start, start).
    goalState : state = s(target, target, target, target).
 
domains
    cargo = alone; wolf; goat; cabbage.
    move = m(cargo Cargo, bank After).
 
class predicates
    solve : (state InitState) -> move* MoveList determ.
clauses
    solve(Init) = MoveList :-
        MoveList = solve1(Init, [Init]),
        !.
 
class predicates
    solve1 : (state S1, state* PreviousStates) -> move* MoveList nondeterm.
clauses
    solve1(goalState, _PreviousStates) = [] :- % solved
        !.
    solve1(S1, PreviousStates) = [Move | MoveList] :-
        % make one step
        Move = move_nd(S1, S2),
        % avoid illegal states
        not(illegalState(S2)),
        % 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.
clauses
    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.
clauses
    oppositeBank(start) = target.
    oppositeBank(target) = start.
 
class predicates
    illegalState : (state State) determ.
clauses
    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).
 
clauses
    run():-
        console::init(),
        if MoveList = solve(initState) then
            foreach m(A,B) = list::getMember_nd(MoveList) do
                stdio::writef("Sail % to %\n", A, B)
            end foreach
        else
            stdio::write("No solution")
        end if.
 
end implement main