listprologwater-jug-problem

Swi Prolog - Implementing water Jug Program with Lists


sorry about before, new to posting in this, trying to get this list to recurse through each one of the actions, keeping it's visited list items and then check in a recursion if the element is not in the list that state would be changed to. I need to find out why this is not working as expected, it seems to get results 7,0, 0,0, 0,4 and 7,4 and the check seems to show the same 4 outcomes in the one run of this program, randomly spitting one of those out, is the action order wrong as I know you have to go from hardest condition to match, the goal is to fill this using call solve(state(0,0)). Then you should get 5 in the jug in the end call, and print out the list that shows the path it used and then find another solution, as there can only be two where there is 5L in the 7L jug.

action(state(L,R), P, state(7,R)) :- 
    L < 4, 
    not(lists:member(state(7,R),P)), 
    print(state(7,R)).
action(state(L,R), P, state(L,4)) :- 
    R < 4, 
    not(lists:member(state(L,4),P)), 
    print(state(L,4)).
action(state(L,R), P, state(0,R)) :- 
    L > 0, 
    not(lists:member(state(0,R),P)), 
    print(state(0,R)).
action(state(L,R), P, state(L,0)) :- 
    R > 0, 
    not(lists:member(state(L,0),P)), 
    print(state(L,0)).
action(state(L,R), P, state(7,C)) :- 
    not(lists:member(state(7,C),P)), 
    C is L + R -7,  
    C > 7,   
    print(state(7,C)).
action(state(L,R), P, state(C,4)) :- 
    not(lists:member(state(C,4),P)), 
    C is L + R -4, 
    C > 4, 
    print(state(C,4)).
action(state(L,R), P, state(0,C)) :- 
    not(lists:member(state(0,C),P)), 
    C is L + R, 
    C @=< 4, 
    print(state(0,C)).
action(state(L,R), P, state(C,0)) :- 
    not(lists:member(state(C,0),P)), 
    C is L + R, 
    C @=< 7, 
    print(state(C,0)).

solve(X) :- 
    reachedgoal(X,[],A).

reachedgoal(state(5,_),L,L).
reachedgoal(State1,P,L) :- 
    action(State1,P,State2),   
    not(lists:member(State2,P)), 
    reachedgoal(State2,[State1|P],L).

Solution

  • There were some little logical mistakes, and it does not make sense to check for duplicate action before you have calculated the values.

    solve(X) :-
      reachedgoal(X,[],_).
    
    reachedgoal(state(5,_),L,L).
    reachedgoal(State1,P,L) :-
      action(State1,P,State2),
      \+ member(State2,P),
      reachedgoal(State2,[State1|P],L).
    
    action(state(_L,R),P,state(7,R)) :- % fill left jug (7l)
      \+ member(state(7,R),P),
      print(state(7,R)),nl.
    action(state(L,_R),P,state(L,4)) :- % fill right jug (4l)
      \+ member(state(L,4),P),
      print(state(L,4)),nl.
    
    action(state(_L,R),P,state(0,R)) :- % empty left jug
      \+ member(state(0,R),P),
      print(state(0,R)),nl.
    action(state(L,_R),P,state(L,0)) :- % empty right jug
      \+ member(state(L,0),P),
      print(state(L,0)),nl.
    
    action(state(L,R),P,state(7,C)) :- % from right jug to left jug until left full
      C is L + R - 7,
      C > 0, C =< 4,
      \+ member(state(7,C),P),
      print(state(7,C)),nl.
    action(state(L,R),P,state(C,4)) :- % from left jug to right jug until right full
      C is L + R - 4,
      C > 0, C =< 7,
      \+ member(state(C,4),P),
      print(state(C,4)),nl.
    
    action(state(L,R),P,state(0,C)) :- % from left jug to right jug until left empty
      C is L + R,
      C =< 4,
      \+ member(state(0,C),P),
      print(state(0,C)),nl.
    action(state(L,R),P,state(C,0)) :- % from right jug to left jug until right empty
      C is L + R,
      C =< 7,
      \+ member(state(C,0),P),
      print(state(C,0)),nl.
    

    This produces:

    | ?- solve(state(0,0)).
    state(7,0)
    state(7,0)
    state(7,4)
    state(7,4)
    state(0,4)
    state(0,4)
    state(4,0)
    state(4,4)
    state(4,4)
    state(7,1)
    state(7,1)
    state(0,1)
    state(0,1)
    state(1,0)
    state(1,4)
    state(1,4)
    state(5,0)
    yes
    

    The duplications are because of insufficient conditions in the actions (see below).

    Checking for duplicate state in each of the actions in wasteful, as you are also checking it in reachedgoal/3, and it is nicer and cleaner to just print the list at the end.

    solve :-
      solve(state(0,0),RevStates), reverse(RevStates,States),
      write(States), nl.
    
    solve(X,States) :-
      reachedgoal(X,[],States).
    
    reachedgoal(state(5,_),L,L).
    reachedgoal(State1,P,L) :-
      action(State1,State2),
      \+ member(State2,P),
      reachedgoal(State2,[State1|P],L).
    
    action(state(L,R),state(7,R)) :- % fill left jug (7l)
      L < 7.
    action(state(L,R),state(L,4)) :- % fill right jug (4l)
      R < 4.
    action(state(L,R),state(0,R)) :- % empty left jug
      L > 0.
    action(state(L,R),state(L,0)) :- % empty right jug
      R > 0.
    action(state(L,R),state(7,C)) :- % from right jug to left jug until left full
      R > 0, L < 7,
      C is L + R - 7, C > 0, C =< 4.
    action(state(L,R),state(C,4)) :- % from left jug to right jug until right full
      L > 0, R < 4,
      C is L + R - 4, C > 0, C =< 7.
    action(state(L,R),state(0,C)) :- % from left jug to right jug until left empty
      L > 0, C is L + R, C =< 4.
    action(state(L,R),state(C,0)) :- % from right jug to left jug until right empty
      R > 0, C is L + R, C =< 7.
    

    Now there are no duplicates:

    | ?- solve.
    [state(0,0),state(7,0),state(7,4),state(0,4),state(4,0),state(4,4),state(7,1),state(0,1),state(1,0),state(1,4)]
    yes