why this water jugs problem doesn't have solution

106 Views Asked by At

I'm new in Prolog. I found a code that solved water with three jugs with goal = 4, capacity1 = 3, capacity2 = 5 and capacity3 = 8, also the jug with capacity = 8 is filled with water. I decide to remove the jug with capacity 8 and add a fill procedure that can fill jugs with capacity 3 and 5. The problem now is that for some reason the code can't find the solution with goal = 4 and in that case the code call procedure solution(_, _) which is fail.

find_solution :-
    initial_state(Initial),
    write('== Starting Search =='), nl,
    solution([[Initial]], StateList),
    length(StateList, Len),
    Transitions is Len - 1,
    format('~n** FOUND SOLUTION of length ~p **', [Transitions]), nl,
    showlist(StateList).

% Remove the cut operator to find more than one solution.
find_solution.

% Base case for finding a solution.
solution(StateLists, StateList) :-
    member(StateList, StateLists),
    last(StateList, Last),
    goal_state(Last),
    report_progress(StateLists, final).

% Recursive rule that looks for a solution by extending each of the generated state lists to add a further state.
solution(StateLists, StateList) :-
    report_progress(StateLists, ongoing),
    extend(StateLists, Extensions),
    solution(Extensions, StateList).

solution(_, _) :-
    write('!! Cannot extend statelist !!'), nl,
    write('!! FAILED: No plan reaches a goal  !!'), nl,
    fail.

% Extend each statelist in a set of possible state lists.
extend(StateLists, ExtendedStateLists) :-
    setof(
        ExtendedStateList,
        StateList ^ Last ^ Next ^ (
            member(StateList, StateLists),
            last(StateList, Last),
            transition(Last, Next),
            legal_state(Next),
            no_loop_or_loopcheck_off(Next, StateLists),
            append(StateList, [Next], ExtendedStateList)
        ),
        ExtendedStateLists
    ).

no_loop_or_loopcheck_off(_, _) :- loopcheck(off), !.
no_loop_or_loopcheck_off(Next, StateLists) :-
    \+(already_reached(Next, StateLists)).

already_reached(State, StateLists) :-
    member(StateList, StateLists),
    member(State1, StateList),
    equivalent_states(State, State1).

showlist([]).
showlist([H | T]) :- write(H), nl, showlist(T).

report_progress(StateLists, Status) :-
    length(StateLists, NS),
    StateLists = [L | _],
    length(L, N),
    Nminus1 is N - 1,
    write('Found '), write(NS),
    write(' states reachable in path length '), write(Nminus1), nl,
    (Status = ongoing ->
        (write('Computing extensions of length : '), write(N), nl)
        ; true
    ).

%%% Initially jugs a and b are empty,
%%% State representation will be as follows:
%%% A state is a list:  [how_reached, Jugstate1, Jugstate2]
%%% Where each JugstateN is a list of the form: [jugname, capcity, content]
initial_state([initial, [a, 3, 0], [b, 5, 0]]).

%% Define goal state to accept any state where one of the
%% jugs contains 4 liters of water:
goal_state([_, [a, _, _], [b, _, 4]]).
goal_state([_, [a, _, 4], [b, _, _]]).

%%% The state transitions are "pour" operations, where the contents of
%%% one jug are poured into another jug up to the limit of the capacity
%%% of the recipient jug.
%%% There are six possible pour actions from one jug to another:
transition([_, A1], [fill_a, A2]) :- fill(A1, A2).
transition([_,B1], [fill_b, B2]) :- fill(B1, B2).
transition([_, A1, B1], [pour_a_to_b, A2, B2]) :- pour(A1, B1, A2, B2).
transition([_, A1, B1], [pour_b_to_a, A2, B2]) :- pour(B1, A1, B2, A2).


fill([Jug1, Capacity1, 0], [Jug1, Capacity1, Initial1]
    ) :-
    Initial1 is Capacity1.

%fill_a :-
%    retractall(initial_state(_)),
%    assertz(initial_state([initial, [a, 3, 0], [b, 5, 5]])).
%fill_b :-
%    retractall(initial_state(_)),
%    assertz(initial_state([initial, [a, 3, 3], [b, 5, 0]])).

%%% The pour operation is defined as follows:
% Case where there is room to pour the full contents of Jug1 to Jug2
% so Jug 1 ends up empty, and its contents are added to Jug2.
pour([Jug1, Capacity1, Initial1], [Jug2, Capacity2, Initial2], % initial jug states
     [Jug1, Capacity1, 0], [Jug2, Capacity2, Final2]            % final jug states
    ) :-
    Initial1 =< (Capacity2 - Initial2),
    Final2 is Initial1 + Initial2.

% Case where only some of Jug1 contents fit into Jug2
% Jug2 ends up full, and some water will be left in Jug1.
pour([Jug1, Capacity1, Initial1], [Jug2, Capacity2, Initial2], % initial jug states
     [Jug1, Capacity1, Final1], [Jug2, Capacity2, Capacity2]    % final jug states
    ) :-
    Initial1 > (Capacity2 - Initial2),
    Final1 is Initial1 - (Capacity2 - Initial2).

%% Define the other helper predicates
legal_state(_).             % All states that can be reached are legal
equivalent_states(X, X).    % Only identical states are equivalent.
loopcheck(on).              % Don't allow search to go into a loop.

%% Call this goal to find a solution.
%find_solution.

fill_a and fill_b should also fill the jugs, but with them I also receive the same result which is: == Starting Search == Found 1 states reachable in path length 0 Computing extensions of length : 1 Found 2 states reachable in path length 1 Computing extensions of length : 2 !! Cannot extend statelist !! !! FAILED: No plan reaches a goal !! !! Cannot extend statelist !! !! FAILED: No plan reaches a goal !! true

1

There are 1 best solutions below

0
On

thanks to the comment of the question to use trance I finally fix the the code. The problem was in fill procedure and I also add empty procedure.

transition([_, A1,B1], [fill_a, A2,B1]) :- fill(A1, B1, A2,B1).
transition([_,A1,B1], [fill_b, A1,B2]) :- fill(A1,B1, A1,B2).
transition([_, A1,B1], [empty_a, A2,B1]) :- empty(A1, B1, A2,B1).
transition([_,A1,B1], [empty_b, A1,B2]) :- empty(A1,B1, A1,B2).
transition([_, A1, B1], [pour_a_to_b, A2, B2]) :- pour(A1, B1, A2, B2).
transition([_, A1, B1], [pour_b_to_a, A2, B2]) :- pour(B1, A1, B2, A2).

%fill A
fill([Jug1, Capacity1, 0], [Jug2, Capacity2, Initial1], % initial jug states
     [Jug1, Capacity1, Initial2], [Jug2, Capacity2, Initial1]            % final jug states
    ) :-
    Initial2 is Capacity1.

% fillB
fill([Jug1, Capacity1, Initial1], [Jug2, Capacity2, 0], % initial jug states
     [Jug1, Capacity1, Initial1], [Jug2, Capacity2, Initial2]            % final jug states
    ) :-
    Initial2 is Capacity2.

% empty A
empty([Jug1, Capacity1, _], [Jug2, Capacity2, Initial2], % initial jug states
     [Jug1, Capacity1, Final1], [Jug2, Capacity2, Initial2]            % final jug states
    ) :-
    Final1 is 0.

% Empty B
empty([Jug1, Capacity1, Initial1], [Jug2, Capacity2, _], % initial jug states
     [Jug1, Capacity1, Initial1], [Jug2, Capacity2, Final1]            % final jug states
    ) :-
    Final1 is 0.