简体   繁体   中英

The Farmer's puzzle - recursive rules and accumulators break my approach

I started learning Prolog a few hours ago, and I'm quite stuck trying to implement a solver for the Farmer problem . I know that there are many examples in the net, but for the purposes of learning I'd like to understand why my code doesn't work, whether or not the approach is valid, and what's the correct way to reason a problem like this .

See below the code. What I've done is:

  • Define the rules that indicate whether a state is valid (safe, from the point of view of the farmer :) )
  • Define the rules that indicate whether a transition is valid and safe
  • Define the rules that represent a valid trip

And what I've achieved so far is:

  1. If I test the trip rule by providing potential solutions, it behaves correctly
  2. If I question the trip rule, it only finds a solution if I shorten the problem to three steps , eg trip( state(s,s,s,s), State(s,n,s,s), R)

I think I have found the problem , please correct me if I'm wrong: if the solution requires more than 3 steps, the last trip rule evaluates at least twice, and after the first recursive execution the PreviousStates accumulator is not empty. When unifying? the explored answer, the not(member(Next,PreviousStates)) then fails because the Next state contains variables that will match what's already in the head of the PreviousStates list.

So, my questions are:

  1. Are my conclusions correct? If not, what's the real problem?
  2. If I'm right in the previous point, how can I solve this? Maybe I'm wrong, but the approach I've taken seems quite logical to me. Where did I failed? Do I have to completely change the approach to the problem?

Thanks in advance for your help!

%Define what are the unsafe states for the Farmer, Goat, Cabbage and Wolf
unsafeState(F,G,C,W) :-
    (C=G, not(C=F));
    (G=W, not(G=F)).
safeState(F,G,C,W) :- not(unsafeState(F,G,C,W)).

%Define what are the valid and safe transitions
isSafeTransition(state(F,F,C,W),state(Fend,Fend,C,W)) :- safeState(F,F,C,W), safeState(Fend,Fend,C,W).
isSafeTransition(state(F,G,F,W),state(Fend,G,Fend,W)) :- safeState(F,G,F,W), safeState(Fend,G,Fend,W).
isSafeTransition(state(F,G,C,F),state(Fend,G,C,Fend)) :- safeState(F,G,C,F), safeState(Fend,G,C,Fend).
isSafeTransition(state(F,G,C,W),state(Fend,G,C,W))    :- safeState(F,G,C,W), safeState(Fend,G,C,W).

% Initial matching rule
trip(A,B,Path):- trip(A,B,Path, []).

% Finishing rule
trip(CurrentState, EndState,Path, _):-
    [CurrentState| [EndState|[]] ] = Path,
    isSafeTransition(CurrentState, EndState).

trip(CurrentState,EndState,Path, PreviousStates):-
    [CurrentState|[Next|Tail]] = Path,
    not(member(Next,PreviousStates)),
    isSafeTransition(CurrentState,Next),
    trip(Next,EndState, [Next|Tail], [CurrentState|PreviousStates]).

I've found the problem myself, I'm posting the explanation in case is helpful for someone.

The issue is that there isn't any rule that limits the possible values for the positions . Basically I've forgotten to limit the possible values to be n or s ...

When solving the query, the potential values for the variables are infinite. That's why a state containing variables is stored in the accumulator and matches the next state.

To solve the problem, I've added two facts that define the valid values for the positions , and modified the rule that controls whether a state is valid or not to include these values.

Find below the fixed program:

% Define the valid sides for any participant    
side(n).
side(s).

% Define what are the valid states for the Farmer, Goat, Cabbage and Wolf
unsafeState(F,G,C,W) :-
    (C=G, not(C=F));
    (G=W, not(G=F)).
validState(F,G,C,W) :-
    side(F),side(G),side(C),side(W),
    not(unsafeState(F,G,C,W)).

%Define what are the valid and safe transitions
isSafeTransition(state(F,F,C,W),state(Fend,Fend,C,W)) :- 
    validState(F,F,C,W), validState(Fend,Fend,C,W).
isSafeTransition(state(F,G,F,W),state(Fend,G,Fend,W)) :- 
    validState(F,G,F,W), validState(Fend,G,Fend,W).
isSafeTransition(state(F,G,C,F),state(Fend,G,C,Fend)) :- 
    validState(F,G,C,F), validState(Fend,G,C,Fend).
isSafeTransition(state(F,G,C,W),state(Fend,G,C,W))    :- 
    validState(F,G,C,W), validState(Fend,G,C,W).

% Initial matching rule
trip(A,B,Path):- trip(A,B,Path, []).

% Finishing rule
trip(CurrentState, EndState,Path, _):-
    [CurrentState| [EndState|[]] ] = Path,
    isSafeTransition(CurrentState, EndState).

trip(CurrentState,EndState,Path, PreviousStates):-
    [CurrentState|[Next|Tail]] = Path,
    not(member(CurrentState,PreviousStates)),
    isSafeTransition(CurrentState,Next),
    trip(Next,EndState, [Next|Tail], [CurrentState|PreviousStates]).

I'm still a total newbie in Prolog, so if anyone can contribute with any correction, please do it!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM