Данная задача встречается в сборниках по занимательной ма-
математике, начиная с XVIII века, и звучит следующим образом.
Крестьянину надо через речку перевезти волка, козу и капусту.
В лодке может поместиться только один человек, а с ним или волк,
или коза, или капуста. Если оставить волка с козой без человека,
то волк съест козу; если оставить козу с капустой, то коза съест
капусту. В присутствии же человека коза не может съесть капусту,
волк - козу. Человек все-таки перевез свой груз через речку. Как
он это сделал?
Для формального описания задачи используется модель графа про-
странства состояний, являющейся базовой для решения задач поиска
в пространстве состояний [2]. Вершины графа соответствуют состоя-
ниям моделируемой системы. Дуга между двумя вершинами существует
только в том случае, если возможен переход между состояниями,
соответствующими этим вершинам. Состояния описываются при по-
мощи структуры STATE, а дуги переходов - предикатом move. Состо-
яние исследуемой системы определяется местонахождением объектов -
человека, волка, козы и капусты :
state( <Человек>, <Волк>, <Коза>, <Капуста> ).
Решение задачи заключается в поиске пути из заданного начально-
го состояния в искомое конечное при помощи применения последо-
вательности правил переходов. Некоторые из состояний системы
запрещены. Запрещенные состояния определяются при помощи пре-
диката unsafe.
К особенности задачи можно отнести требование однократного
попадания в любое из разрешенных состояний, проходимых в процес-
се поиска решения. С целью контроля данного требования выполня-
ется сохранение пройденных состояний в списке PATH и его контроль
при помощи предиката path.
Приводимую ниже программу можно рассматривать как базовую
для решения задач методом поиска на графах пространства состоя-
ний. Для данного случая используется четырехмерное пространство
состояний, объекты в котором описываются структурой
STATE(LOC,LOC,LOC,LOC).
Тип данных LOC определяет оцифровку осей координат простран-
ства и в данном случае имеет два значения, соответствующих левому
и правому берегам реки.
Дополнительные предикаты имеют следующие назначения:
- opposite - опреляет противоположные берега (используется
в правиле описания перехода из одного состояния в другое);
- member - определяет принадлежность состояния списку прой-
денного пути;
- go - определяет цель для раздела goal.
Остальные предикаты служат для отображения процесса решения
на экран монитора. Для этого используются четыре окна, определен-
ные в разделе goal. Решение выполняется по шагам. Для выполнения
следующего шага необходимо нажать любую клавишу.
Ниже приводится текст программы LOGTASK1.PRO на языке Турбо
Пролог.
#MDOMAINS
LOC = right ; left % Два берега - правый и левый
STATE = state(LOC,LOC,LOC,LOC) % Местонахождение объектов
PATH = STATE* % Последовательность пройденных состояний
PREDICATES
go(STATE,STATE) % Начало решения задачи
path(STATE,STATE,PATH,PATH) % Путь из одного состояния в другое
move(STATE,STATE) % Перемещение объектов с одного берега на другой
opposite(LOC,LOC) % Противоположные берега
unsafe(STATE) % Недопустимое состояние
member(STATE,PATH) % Проверить : встречалось ли такое состояние ?
tran1(LOC,string) % Перевод местонахождения объекта LOC
tran2(LOC,string) % в строку символов для выдачи сообщений
write_path(PATH)
write_move(STATE,STATE)
show_move(STATE,STATE)
showside(LOC,LOC,string)
s_river
GOAL
makewindow(4,$0,$0,"",0,0,24,80),
makewindow(3,$1E,$1E," Задача о крестьянине, волке, козе и капусте ",
0,0,9,80,1,255,"╓╖╙╜─║"),
makewindow(2,$97,0,"",24,0,1,80),
write(" Для выполнения следующего шага нажмите любую клавишу"),
makewindow(1,$1F,$1E," Решение ",11,0,11,80,1,1,"╓╖╙╜─║"),
show_move(state(left,left,left,left),state(left,left,left,left)),
go(state(right,right,right,right),state(left,left,left,left)),
write(" Решение получено. "),
shiftwindow(2),nl,
write(" Нажмите любую клавишу ..."),
readchar(_),
exit.
CLAUSES
go(S,G):-
path(S,G,[S],L),
write(" Последовательность перевозок :"),nl,
write_path(L),
fail.
go(_,_).
path(S,G,L,L1) :- move(S,S1),
not( unsafe(S1) ),
not( member(S1,L) ),
path( S1,G,[S1|L],L1),!.
path(G,G,T,T):- !. % Искомое состояние достигнуто
/* Возможные варианты перевозок объектов */
move(state(X,X,G,C),state(Y,Y,G,C)):-opposite(X,Y). % Человек с Волком
move(state(X,W,X,C),state(Y,W,Y,C)):-opposite(X,Y). % Человек с Козой
move(state(X,W,G,X),state(Y,W,G,Y)):-opposite(X,Y). % Человек с Капустой
move(state(X,W,G,C),state(Y,W,G,C)):-opposite(X,Y). % Один Человек
opposite(right,left).
opposite(left,right):-!.
/* Запрещенные состояния системы объектов */
unsafe( state(F,X,X,_) ):- opposite(F,X). % Волк съедает Козу
unsafe( state(F,_,X,X) ):- opposite(F,X). % Коза съедает капусту
member(X,[X|_]).
member(X,[_|L]):-member(X,L).
/* Сообщения о перемещениях объектов */
write_path( [H1,H2|T] ) :- !,
readchar(_),
write_move(H1,H2),
show_move(H1,H2),
write_path([H2|T]).
write_path( [] ).
tran1(right,"правого берега").
tran1(left,"левого берега").
tran2(right,"правый берег").
tran2(left,"левый берег").
write_move( state(X,W,G,C), state(Y,W,G,C) ) :-!,
tran1(X,A),tran2(Y,B),
write(" Человек переправляется с ",A," на ",B),nl.
write_move( state(X,X,G,C), state(Y,Y,G,C) ) :-!,
tran1(X,A),tran2(Y,B),
write(" Человек перевозит волка с ",A," реки на ",B),nl.
write_move( state(X,W,X,C), state(Y,W,Y,C) ) :-!,
tran1(X,A),tran2(Y,B),
write(" Человек перевозит козу с ",A," реки на ",B),nl.
write_move( state(X,W,G,X), state(Y,W,G,Y) ) :-!,
tran1(X,A),tran2(Y,B),
write(" Человек перевозит капусту с ",A," реки на ",B),nl.
show_move( state(F,W,G,C), state(F1,W1,G1,C1) ) :-!,
s_river,
showside(F,F1,"Человек"),
showside(W,W1," Волк "),
showside(G,G1," Коза "),
showside(C,C1,"Капуста"),
shiftwindow(1).
showside(right,right,Item):-!,
write(" ~~~~~ ",
Item),nl.
showside(left,left,Item):-!,
write(" ",Item," ~~~~~ "),nl.
showside(right,left,Item):-!,
write(" ",Item," <= <= <= ",Item),nl.
showside(left,right,Item):-!,
write(" ",
Item," => => => ",Item),nl.
s_river:-
shiftwindow(3),clearwindow,
write(" Левый берег Река ",
" Правый берег"),nl,
write(" ──────────────────────────────────",
"──────────────────────────────────────"),
nl.#P
|