Что не так с моей прологовой программой для решения головоломки с тремя кувшинами с водой?

Может ли кто-нибудь найти, почему у меня нет верных ответов с моим «переходом» к этому коду? Например, я пишу go(7,3,l) и предполагаю, что он должен переместить 3 литра воды во второй кувшин, но согласно прологу это неверно. Что случилось?

:- dynamic go/3.
:- dynamic cur_state/1,init/5.
:- dynamic end_state/1, final/5.

cur_state(State):-State = state(10,0,0,7,l).
end_state(State):-State = state(0,3,3,0,r).

pour(state(D1,D2,D3,n,l),move(D,C,r),state(D,C,D3,n,r)) :-
        D is D1-n,
        C is D2+n.
pour(state(D1,D2,D3,n,r),move(D,C,l),state(D,C,D3,n,l)) :-
        D is D1-n,
        C is D2.
pour(state(D1,D2,D3,n,l),move(D,C,r),state(D,D2,C,n,r)) :-
        D is D1-n,
        C is D3+n.
pour(state(D1,D2,D3,n,l),move(D,C,r),state(D1,D,C,n,r)) :-
        D is D2-n,
        C is D3+n.
pour(state(D1,D2,D3,n,r),move(D,C,l),state(D1,D,C,n,l)) :-
        D is D2-n,
        C is D1+n.
pour(state(D1,D2,D3,n,r),move(D,C,l),state(D1,D,c,n,l)) :-
        D is D2-n,
        C is D3.
pour(state(D1,D2,D3,n,l),move(D,C,r),state(C,D2,D,n,r)) :-
        D is D3-n,
        C is D1.
pour(state(D1,D2,D3,n,r),move(D,C,l),state(D1,C,D,n,l)) :-
        D is D3-n,
        C is D2+n.
pour(state(D1,D2,D3,n,r),move(D,C,l),state(C,D2,D,n,l)) :-
        D is D3-n,
        C is D1+n.

carry(7,0).
carry(3,0).
carry(10,0).
carry(7,0).
carry(7,3).

legal(10,X,Y):-X+Y<10.
legal(X,Y,Z):-X+Y+Z<10.
legal(X,7,Y):-X+Y=<3.
legal(X,Y,3):-X+Y=<7.

newstate(state(D1,D2,D3,n,l),state(D11,D22,D33,n1,r)):-
        carry(M,C),
        M=<7,C=<3,
        D22 is D2+n,
        D11 is D1-n,
    D3 is D33,
    n1 is n,
        D2=<7,D1=<10,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,r),state(D11,D22,D33,n1,l)):-
        carry(M,C),
        M=<10,C=<100,
        D11 is D1-n,
    D22 is D2,
    D33 is D3,
        D1=<10,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,l),state(D11,D22,D33,n1,r)):-
        carry(M,C),
        M=<10,C<3,
        D11 is D1-n,
        D33 is D3+n,
    D22 is D2,
        D1=<10,D3=<3,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,r),state(D11,D22,D33,n1,l)):-
        carry(M,C),
        M=<7,C=<3,
        D22 is D2-n,
        D33 is D1+n,
        D11 is D1,
    D2=<7,D1=<10,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,l),state(D11,D22,D33,n1,r)):-
        carry(M,C),
        M=<7,C=0,
        D22 is D2-n,
        D33 is D3+n,
        D11 is D1,
    D2=<7,D3=<3,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,r),state(D11,D22,D33,n1,l)):-
        carry(M,C),
        M=<7,C=<100,
        D22 is D2-n,
    D33 is D3,
    D11 is D1,    
    D2=<7,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,r),state(D11,D22,D33,n1,l)):-
        carry(M,C),
        M=<3,C=<7,
        D22 is D2+n,
        D33 is D3-n,
        D11 is D1,
    D3=<3,D2=<7,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,r),state(D11,D22,D33,n1,l)):-
        carry(M,C),
        M=<3,C=<100,
        D11 is D1+n,
        D33 is D3-n,
        D22 is D2,
    D3=<3,D1=<10,
    legal(D1,D2,D3).

newstate(state(D1,D2,D3,n,l),state(D11,D22,D33,n1,r)):-
        carry(M,C),
        M=<3,C=<100,
        D33 is D3-n,
        D22 is D2,
    D11 is D1,  
    D3=<3,
    legal(D1,D2,D3).


eisodos(_):- cur_state(State),write(State),nl.

init(S1,S2,S3,S4,S5):-assert(cur_state(State):-State = state(S1,S2,S3,S4,S5)),write('Arxikh:'),
   write(state(S1,S2,S3,S4,S5)),retractall(init(S1,S2,S3,S4,S5)),nl.

final(S1,S2,S3,S4,S5):-assert(end_state(State):-State = state(S1,S2,S3,S4,S5)),write('Telikh:'),
   write(state(S1,S2,S3,S4,S5)),retractall(init1(S1,S2,S3,S4,S5)),nl.

go(Move1,Move2,Move3):-cur_state(State),newstate(State,NextState),
        pour(State,move(Move1,Move2,Move3), NextState),
        retractall(cur_state(State):-State = state(_,_,_,_,_)),asserta(cur_state(NextState)),
        ((end_state(NextState),write('Bravo!!!!')) ;(write(' ---*Eiste sthn katastash --- :'),write(NextState))),nl.

person tetartos    schedule 05.01.2012    source источник
comment
Глядя на этот код, я не чувствую вкуса к изучению Пролога, извините.   -  person Luc M    schedule 06.01.2012
comment
Я не могу понять, почему ваш предикат go не работает, но я должен признать, что тоже не могу понять, почему ваш предикат go должен работать... Чего вы пытаетесь достичь здесь?   -  person m09    schedule 06.01.2012
comment
я пытаюсь написать программу, которая имитирует задачу о трех кувшинах с водой и возможном перемещении воды между ними.   -  person tetartos    schedule 06.01.2012


Ответы (3)


Первое, что вам нужно исправить, это основной синтаксис: переменные должны начинаться с прописной буквы, поэтому, например, вы должны изменить это правило.

pour(state(D1,D2,D3,n,l),move(D,C,r),state(D,C,D3,n,r)) :-
    D is D1-n,
    C is D2+n.

to

pour(state(D1,D2,D3,N,l),move(D,C,r),state(D,C,D3,N,r)) :-
    D is D1-N,
    C is D2+N.

и

newstate(state(D1,D2,D3,n,l),state(D11,D22,D33,n1,r)):-
        carry(M,C),
        M=<7,C=<3,
        D22 is D2+n,
        D11 is D1-n,
    D3 is D33,
    n1 is n,
        D2=<7,D1=<10,
    legal(D1,D2,D3).

to

newstate(state(D1,D2,D3,N,l),state(D11,D22,D33,N1,r)):-
    carry(M,C),
    M=<7,C=<3,
    D22 is D2+N,
    D11 is D1-N,
    D3 is D33,
    N1 is N,
    D2=<7,D1=<10,
    legal(D1,D2,D3).

Вы должны понимать, что N1 оценивается только в первой процедуре newstate/2: затем исправьте другие экземпляры этого правила.

Затем вы должны удалить бесполезные динамические утверждения: подумайте о вашем фактическом использовании assert/retractall: вам нужно изменить состояние, перемещая «воду» между «кувшинами» на любом этапе: поэтому вы должны утверждать/ retract state/5 во время зацикливания на ходу, от начального состояния (подтвердить это при запуске программы) до конечного приемлемого состояния, которое будет проверено на ходу, чтобы остановить цикл.

Но этот стиль, основанный на изменении состояния, приводит к очень трудной отладке. Вместо этого попробуйте полностью удалить динамику, утвердить/убрать все и передать состояние в своем цикле.

person CapelliC    schedule 06.01.2012
comment
Спасибо за ваши ответы, но если вы не возражаете, прочитайте комментарий, который я написал @Mog, и любые идеи, кроме этих, будут полезны ... - person tetartos; 18.01.2012
comment
Вы пытались исправить свою программу, используя подсказки, которые я написал? Потому что любой шаг, который я предприму, будет идти по этому пути. Конечно, более простым способом решения вашей проблемы может быть адаптация (после понимания :) любого из правильных решений от Mog или mat. Чтобы быть правдой, я не пробовал их, но выглядит гораздо более правдоподобно, чем ваш код. Ключом к решению комбинаторных задач (грубым перебором) часто является использование select/3 с соответствующим представлением состояния. - person CapelliC; 18.01.2012
comment
Во-первых, спасибо за ваш ответ. Я внес изменения, которые вы предложили, и у меня есть это, когда я пытаюсь перейти (7,3, r).: ОШИБКА: is/2: Аргументы недостаточно созданы Исключение: (7) новое состояние (состояние (10, 0, 0, 7, л), _G212) ? Но я не уверен, что правильно объяснил, что я хочу от своей программы. Это программа, которая решает проблему с кувшинами для воды, но программа, в которой пользователь «говорит», что делать, и если пользователь говорит правильные движения проблема решается без алгоритмов и т.д.. - person tetartos; 23.01.2012

Расширяя ответ @Mog, я предлагаю использовать итеративное углубление, если вы хотите найти кратчайшее решение. Следующее основано на коде, опубликованном @Mog (и, таким образом, решает ту же проблему, которая немного отличается от проблемы, опубликованной OP). Поскольку мы хотим описать список (ходов), удобна нотация DCG:

solution(Path) :- length(Path, _), phrase(path([0-3, 0-5, 8-8]), Path).

path(State) --> { equivalent(State, [0-3, 4-5, 4-8]) }.
path(State0) --> [From-To],
        { move(State0, State), State = [_-From, _-To, _] },
        path(State).

equivalent(State1, State2) :- forall(member(X, State1), member(X, State2)).

move(State, [NewX-From, NewY-To|NewRest]) :-
    select(X-From, State, Rest),
    X \== 0,
    select(Y-To, Rest, NewRest),
    Fillable is To - Y,
    ToFill is min(X, Fillable),
    NewY is Y + ToFill,
    NewX is X - ToFill.

Пример запроса:

?- solution(Ps).
Ps = [8-5, 5-3, 3-8, 5-3, 8-5, 5-3, 3-8] .
person mat    schedule 06.01.2012

Что ж, как я уже сказал в своем комментарии, я не знаю, как помочь вам с вашей текущей работой, поскольку в ней так много неправильных вещей. Я бы посоветовал вам прочитать хороший учебник по Prolog (например, Изучите Prolog сейчас), чтобы вы могли основы языка. Вот простой способ решить вашу проблему, если вы заинтересованы. Если вы не хотите, чтобы ваша проблема была испорчена, просто не читайте дальше : ] (то, что я опубликовал, касается кувшинов 3/5/8 и разделения 4/4).

go(Path) :-
    solve([0-3, 0-5, 8-8], [], [], Temp),
    reverse(Temp, Path).

solve(State, _Visited, Path, Path) :-
    equivalent(State, [0-3, 4-5, 4-8]).
solve(State, Visited, Acc, Path) :-
    move(State, NewState),
    NewState = [_-From, _-To|_],
    forall(member(Past, Visited), \+ equivalent(Past, NewState)),
    solve(NewState, [NewState|Visited], [From-To|Acc], Path).

equivalent(State1, State2) :-
    forall(member(X, State1), member(X, State2)).

move(State, [NewX-From, NewY-To|NewRest]) :-
    select(X-From, State, Rest),
    X \== 0,
    select(Y-To, Rest, NewRest),
    Fillable is To - Y,
    ToFill is min(X, Fillable),
    NewY is Y + ToFill,
    NewX is X - ToFill.

Если вам нужны пояснения по коду после того, как вы прочитали немного больше о прологе, не стесняйтесь!

person m09    schedule 05.01.2012
comment
Прежде всего спасибо! Но моя проблема в том, что я хочу, чтобы пользователь «написал» путь, вызвав «функцию», которую я пытался сделать. Например, я хотел, чтобы в моей программе пользователь написал go(7,3,0,3,r) и 3 литра, чтобы пойти во второй кувшин. Любые идеи по этому поводу?? - person tetartos; 18.01.2012