Ответ 1
Для этой проблемы явное время не требуется, поэтому мы будем представлять историю как список действий. С другой стороны, вам нужно явно указать состояние вашей системы, то есть текущее содержимое трех ведер. Причина в том, что структуры данных Prolog (т.е. Термины) не могут быть изменены после их создания. Поскольку существует много бессмысленных терминов, рекомендуется сначала определить типы данных с помощью предиката is_type/1
. Поскольку в какой-то момент вы будете использовать арифметику (когда вы выливаете воду из одного ведра в другой), я буду использовать арифметические ограничения вместо древнего предиката is/2
.
:- use_module(library(clpfd)).
Сначала укажем, что есть 3 ведра, представленные атомами b1, b2 и b3:
is_bucket(b1).
is_bucket(b2).
is_bucket(b3).
Тогда нам нужно определить наше состояние. Мы просто используем термин buckets/3
, где первый аргумент содержит емкость b1 и аналогично для двух других.
is_state(buckets(X,Y,Z)) :-
% each bucket contains at least 0 liters
[X,Y,Z] ins 0 .. sup.
Все контейнеры могут не стать отрицательными, поэтому мы устанавливаем их диапазон в диапазоне от нуля до (положительной) бесконечности.
Теперь какое действие? Пока вы описали опорожнение и заливку:
is_action(empty(B)) :-
is_bucket(B).
is_action(pour(From, To)) :-
is_bucket(From),
is_bucket(To).
Чтобы освободить ведро, нам нужно только знать, какой из них. Если мы выливаем воду из одного в другой, нам нужно описать и то, и другое. Поскольку у нас уже есть предикат, описывающий ведро, мы можем просто указать правило как "Если From
и To
являются ведрами, то pour(From, To)
является действием.
Теперь нам нужно объяснить, как действие трансформирует состояние. Это отношение между старым состоянием, новым состоянием и потому, что мы хотели бы знать, что происходит, а также историю.
% initial state
state_goesto_action(buckets(7,5,3), buckets(7,5,3), []).
Переход для исходного состояния ничего не меняет и имеет пустую историю ([]
).
% state transitions for moving
state_goesto_action(buckets(X,Y,Z), buckets(0,Y,Z), [empty(b1) | History]) :-
state_goesto_action(_S0, buckets(X,Y,Z), History).
Это правило можно прочитать как "Если бы у нас было действие, исходящее из некоторого состояния _S0
, ведущего к состоянию buckets(X,Y,Z)
с некоторым History
, то мы можем выполнить действие empty(b1)
next, и мы достигнем состояние buckets(0,Y,Z)
". Другими словами, состояние обновляется, и действие добавляется к истории. Симметричное правило работает для других ведер:
state_goesto_action(buckets(X,Y,Z), buckets(X,0,Z), [empty(b2) | History]) :-
state_goesto_action(_S0, buckets(X,Y,Z), History).
state_goesto_action(buckets(X,Y,Z), buckets(X,Y,0), [empty(b3) | History]) :-
state_goesto_action(_S0, buckets(X,Y,Z), History).
Как мы можем проверить, имеет ли это смысл? Давайте посмотрим на истории длины 2:
?- state_goesto_action(_,S1,[H1,H2]).
S1 = buckets(0, 3, 5),
H1 = H2, H2 = empty(b1) .
Хорошо, если оба действия empty(b1)
, первое ведро пустое, а остальные нетронутые. Давайте рассмотрим дальнейшие решения:
S1 = buckets(0, 0, 5),
H1 = empty(b1),
H2 = empty(b2) ;
S1 = buckets(0, 3, 0),
H1 = empty(b1),
H2 = empty(b3) ;
S1 = buckets(0, 0, 5),
H1 = empty(b2),
H2 = empty(b1) ;
S1 = buckets(7, 0, 5),
H1 = H2, H2 = empty(b2) ;
S1 = buckets(7, 0, 0),
H1 = empty(b2),
H2 = empty(b3) ;
S1 = buckets(0, 3, 0),
H1 = empty(b3),
H2 = empty(b1) ;
S1 = buckets(7, 0, 0),
H1 = empty(b3),
H2 = empty(b2) ;
S1 = buckets(7, 3, 0),
H1 = H2, H2 = empty(b3).
Похоже, у нас есть все возможности опорожнения ведер (и ничего более:-)). Теперь вам нужно добавить правила для заливки из одного ведра в другое. Удачи!
(Изменить: опечатки, ошибка во втором правиле)