Глава 12. Игры и головоломки Введение В первых одиннадцати главах Турбо-Пролог использовался для решения задач самых разных типов начиная с простой обработки информации, находящейся в базе данных, и кончая экспертными системами. В этой главе демонстрируется программирование на Турбо-Прологе игр и головоломок. Вы, вероятно знаете насколько сложны техника и методы, используемые для программирования игр и головоломок. Игры и головоломки различной сложности являются хорошей пробой сил для программиста. Несомненно, что программи- рование игр и головоломок улучшает навыки программиста в облас- ти обработки данных разных типов. В этой главе вы познакомитесь с разработкой пяти программ на Турбо-Прологе. Первая - программа для игры в отгадывание числа, вторая и третья реализуют некоторую игру двух лиц. Чет- вертая программа предназначена для решения классической задачи ИИ, а пятая решает логическую задачу. Эта последняя программа демонстрирует средства пользовательского интерфейса, необходи- мого для выполнения программы в более интересной, ориентирован- ной на пользователе форме. В каждой из этих программ средства Турбо-Пролога использу- ются для решения конкретных задач, для которых и предназначены эти программы. Многообразие внутренних подпрограмм Турбо-Проло- га, его графика и эффективность - все это используется совмест- но с целью продемонстрироровать технику программирования, кото- рая может найти примение для широкого класса прикладных задач. 12.1.Решение задач в играх и головоломках Ваша ежедневная деятельность состоит из многих повторяю- щихся действий, таких как приготовление пищи, еда, сон и поезд- ки на работу. Для нормального человека эта деятельность может потребовать большой энергии, но очень небольшого напряжения ум- ственных способностей. Но игры и головоломки требуют размышлений. Это одна из причин их популярности. Задачи, содержащиеся в играх и голово- ломках, хорошо формализованы, поэтому значительная часть первых работ в области искусственного интеллекта была ориентирована на игры и головоломки. Были написаны программы для игры с человеком. Программы для игры в настольные игры, такие как шашки и шахматы, способны обрабатывать логические взаимосвязи, возникающие в процессе иг- ры. Программы, решающие такие известные головоломки как "Крес- тики и нолики" и "Ханойская башня" являются классическими при- мерами, часто встречающимися в литературе по искусственному ин- теллекту. Используя Турбо-Пролог можно разработать и реализовать за- конченную программу для решения головоломки или какой-нибудь игры. Внутренние унификационные подпрограммы Турбо-Пролога спо- собны выполнять многоуровневый поиск и сопоставление с образ- цом, что позволяет легко и эффективно реализовать методы реше- ния задач. Вы, по-видимому, уже знаете, что либо результатом, либо целью вашей будущей работы будет решение некоторых задач. Для многих таких задач метод их решения может потребовать выполне- ния цепочки рассуждений, начинающейся с желаемого результата и закачивающейся заданными условиями. В ИИ этот подход называется 1обратным выводом 0. Турбо-Пролог имеет средства для обратного вы- вода, которые являются его фундаментальным свойством. В общем случае в Прологе, и в Турбо-Прологе в частности, обычно голова правила (или цель) содержит параметры, значения которых неизвестны. Подцели тела этого правила пытаются вычис- лить параметры цели. Именно это является фундаментальным свойс- твом Пролога, и таким образом обратный вывод является весьма "естественным" для Пролога. При решении задач другого типа могут потребоваться все не- обходимые промежуточные шаги и соответствующие условия для то- го, чтобы достичь любого из возможных решений или ответа. В этом случае решение задачи начинается с известных фактов или условий и выполняется как прямой поиск допустимого решения. Этот подход называется 1прямым выводом 0. 12.2. Игры в отгадывание числа Игры в отгадывание числа являются конкретным примером игр, основанных на сопоставлении символов. В программах для игр в отгадывание символов этими символами могут быть числа, литеры, слова или предложения, графические объекты, которые хранятся в памяти машины. Игры в отгадывание чисел особенно интересны тем, что они могут быть организованы так, чтобы обеспечивать различ- ные уровни сложности для играющего. 12.2.1. Анализ проекта При разработке игры в отгадывание числа необходимо устано- вить предел области, в которой могут быть эти числа. Например, рассмотрим игру, цель которой отгадать число из диапазона от 1 до 100. Если вы будете отгадывать число "слепым" образом, то для нахождения правильного ответа может потребоваться около 99 попыток. С другой стороны, если у вас есть продвижения к пра- вильному ответу, то тогда полное число требуемых попыток может быть существенно уменьшено. Если для генерации случайного числа и выдачи советов во время игры используется компьютер, то он участвует в игре пас- сивно. Только вы являетесь "реальным" игроком. Это пример игры с одним игроком. Но можно написать программу, в которой компь- ютер играет активную роль в качестве оппонента. В этом случае, вы и компьютер являетесь полнорпавными игроками. Тогда это игра двух игроков. 12.2.2. План игры В качестве примера игры с одним участником рассмотрим игру, в которой компьютер выбирает целое число из диапазона от 1 до 100. Вы пытаетесь угадать это число и сообщаете его компьютеру. Затем компьютер дает вам знать правилен ли ваш выбор. Если ваш выбор неправилен, то компьютер даст вам знать, что выбранноле вами число больше или меньше, чем задуманное. Продолжая таким образом, вы будете отвечать с возрастающей точ- ностью при каждой попытке. В конце концов ваш ответ будет пра- вильным. Цель в игре такого типа состоит в том, чтобы умудрить- ся угадать правильное число за минимальное число попыток. Эта игра является простой иллюстрацией эвристического под- хода. По сравнению со "слепыми" методами, эвристический подход позволяет вам двигаться к решению задачи, корректируя область поиска по определенным правилам. При детальном анализе этой иг- ры, вы можете надеяться найти эмпирическое правило (или эврис- тику) для выбора соответствующих действий. Например, пусть X (число которое должно быть отгадано) равно 36. В табл. 12.1 показана логическая последователь- ность попыток отгадать число. Таблица 12.1. Логичекая последовательность нахождения числа _______________________________________________________________ 1Попытка Ваш ход Подсказка копьютера
1для выполнения следующей
1попытки 1
50
Введите число поменьше 2
25
Введите число побольше 3
37
Введите число поменьше 4
31
Введите число побольше 5
34
Введите число побольше 6
35
Введите число побольше 7
36
Правильный ответ _______________________________________________________________ Ответы были получены делением оставшейся части области оп- ределения числа X (0 < X <= 100) пополам. Отгадывание любого числа потребует не более 7 попыток. Заметьте, что после четырех попыток оставшаяся область определения есть интервал от 32 до 36, т.е. имеется только пять кандидатов. Вы можете выбрать одно из них случайным образом в качестве пятой попытки. У вас имеет- ся в этот момент 20% -ая вероятность выбрать правильное число. Программа для такой игры реализует следующие шаги: 1. Сге- нерировать случайное число в диапазоне от 1 до 100
включительно. 2. Пригласить игрока ввести целое число в качестве попытки
угадать правильное.
a. Если ответ правильный, выдать соответствующее сооб-
щение и выйти из программы.
b. Если ответ меньше, чем правильный, то выдать соот-
ветствующее сообщение и ввести следующий ответ.
c. Если ответ больше, чем правильный, то выдать соот-
ветствующее сообщение и ввести следующий ответ. Заметьте, что шаг 1 выполняется только один раз, в то вре- мя как шаг 2 выполняется повторно до тех пор, пока не будет по- лучен правильный ответ. 12.2.3. Программа для игры в отгадывание числа Достаточно любопытно написать программу на Турбо-Прологе, реализующую игру в отгадывание числа, но также не менее инте- ресно играть в эту игру. Целое число в диапазоне от 1 до 100 вычисляется при помощи генератора случайных чисел Турбо-Проло- га, а информацию, подсказывающую правильный ответ нетрудно вы- вести на экран. Эту игру можно реализовать на Турбо-Прологе при помощи следующих процедур. Правило для генерации случайного числа следующее: generate_rand(X) :-
random(R),
X = 1 + R * 100,
nl,write(" Я задумал число."),
nl,write(" Теперь Ваш ход!"),nl. Это правило присваивает переменной X значение случайного числа. Встроенный предикат Турбо-Пролога random(R) генерирует случайное действительное число X, где 0 <= X < 1. Предикат ра- венства X = 1 + R * 100 преобразует случайное число в число с областью определения, за- данной для X, т.е. в число в диапазоне от 1 до 100 и преобра- ует действительное число в целое. Для того чтобы запросить ввод пользователя (ответ) и быст- ро его проверить нужно написать следующее правило: play_it_sam(X) :-
nl, write(" Введите ваш ответ. "),
nl, readint(G),
test_and_tell(X,G),
play_it_sam(X). Предикат readint(G) считывает ответ пользователя (G). Пра- вило test_and_tell(X,G) проверяет верен ли ответ пользователя. Как было объяснено выше, существуют три возможности для каждого ответа: число слишком большое, число слишком маленькое и число правильное. Каждый из этих отыетов вызывает определенное прави- ло,подсказывающее пользователю в каком напрвлении двигаться при следующей попытке. Правила на Турбо-Прологе для проверки отве- та следующие: test_and_tell(X,G) :-
X = G,
say_you_got_it_right(X). test_and_tell(X,G) :-
X > G,
say_too_small. test_and_tell(X,G) :-
X < G,
say_too_big. say_too_big :-
nl,write(" Your guess is too big. "),
nl,write(" Try a smaller number. "). say_too_small :-
nl,write(" Your guess is too small. "),
nl,write(" Try a bigger number. "). say_you_got_it_right(X) :-
nl,write("You got it right."),
nl,write(" It is",X,"."),
nl,write(" Good bye !"),
nl,write(" Press the space bar."),
nl,readchar(_),
exit. Теперь можно написать для программы цель play_the_game как правило, которое включает предшествующие два в качестве подце- лей: play_the_game :-
give_info,
play_it. Правило give_info сообщает пользователю как организована игра. Правило play_it выполняет всю игру. Это правило вызывает genеrate_rand(A) для запуска игры: play_it :-
generate_rand(A),
play_it_sam(A). Программа игры в отгадывание числа (листиг 12.1) реализует этот проект.Заметьте, что G и X объявлены как целые цисла, а R объявлено как действительное число. Помимо встроенного предика- та генерации случайных чисел, программа также использует средс- тва Турбо-Пролога для организации окон. Листинг 12.1 На рис. 12.1 показан диалог с программой "Отгадай число". Заметьте, что играющий воспользовался советом компьтера и ис- пользует метод деления отрезка пополам в качестве эвристики. * Упражнения 12.1. Запустите несколько раз программу игры в отгадывание чис- ла. Используйте стратегию деления отрезка пополам для достиже- ния лучших результатов. 12.2. Измените программу игры в отгадывание числа так, чтобы случайное число лежало в диапазоне от1 до 10. Каково максималь- ное число попыток отгадать число в этом случае? 12.3. Запустите модифицированную программу игры в отгадывание числа несколько раз. Используйте стратегию деления отрезка по- полам. Какое у вас наилушее число попыток отгадать число? 12.4. Модифицируйте эту программу так, чтобы она запоминала число попыток пользователя отгадать число. Если это число мень- ше, чем максимально допустимое, то выдайте на экран текст "WELL DONE !" (Хорошо сработано !) в отдельном окне. (Подсказка: для реализации счетчика см. гл. 3 о рекурсии). Рисунок 12.1 12.3. Игра в 23 спички Игра "23 спички" является одной из многих игр для двух лиц. Эта игра весьма интересна, так как она может быть реализо- вана либо как простая игра, либо как весьма сложная со многими вариантами. Вы узнаете как разработать программы на Турбо-Про- логе для обоих случаев. Игра "23 спички" заключается в следующем. Игроки начинают с 23 спичек (или палочек). Каждый игрок по очереди удаляет 1, 2 или 3 спички. Тот, кто возьмет последнюю - проигравший. Таким образом, цель игры - это заставить другого игрока взять послед- нюю спичку. Первая программа на Турбо-Прологе для этой игры будет раз- работана в следующем разделе. 12.3.1. Обсуждение проекта Существуют два варианта игры в 23 спички:
_Вариант с одной кучкой 1Номер кучки Состояние кучки Число спичек 1
1,2,3,...22,23
23
_Вариант с четырьмя кучками 1Номер кучки Состояние кучки Число спичек 1
1 2 3 4 5
5 2
6 7 8 9 10 11 6
3
12 13 14 15 16 17 6 4
18 19 20 21 22 23 6 В том и другом варианте игры каждый игрок по очереди берет 1, 2 или 3 спички из каждой кучки. В обоих вармантах суммарное число спичек, остающиеся после хода каждого игрока впоследствии определяет проигравшего. Для простоты начнем с варианта с одной кучкой. Для реали- зации этой игры на Турбо-Прологе компьютер становится одним из игроков, а пользователь - другим игроком. Основная процедура включает несколько шагов: 1. Выбрать 23 спички в качестве начального числа спичек и
поместить их в единственную кучку. 2. Попросить пользователя удалить 1, 2 или 3 спички. Пос- читать число оставшихся спичек в кучке.
a. Если осталась только одна спичка, то компьютер дол-
жен удалить эту последную спичку. В этом случае
компьютер проигрывает.
b. Если же осталось более одной спички, то перейти к
шагу 3. 3. Компьютер удаляет 1, 2 или 3 спички. Вычислить число
оставшихся спичек в кучке.
a. Если осталась только одна спичка, то пользователь
должен взять эту последнюю спичку. В этом случае
он проигрывает.
b. Если осталось больше одной спички, то повторить
шаг 2. Сама по себе процедура очень простая. Тем не менее число спичек, удаляемое игроком, определяет победителя, и следова- тельно, стратегия является важной компонентой структуры игры. 12.3.2. Программа простой игры в "23 спички" При реализации этой простой игры можно позволить пользова- телю самостоятельно выбирать стратегию игры. Поэтому сейчас на- до позаботиться только о компьютере, как игроке. Компьютер дол- жен генерировать числа 1, 2 или 3 случайным образом и использо- вать это число в игровом модуле. Для написания программы игры "23 спички" необходимо реали- зовать три шага, рассмотренные в предыдущем разделе. Первый шаг - это установить начальное число спичек равным 23. Затем можно передать это число из основного модуля в подмодуль в соответст- вии со структурой программы (см. рис. 12.2). Рисунок 12.2. Основной модуль имеет вид: play_the_game :-
do_windows,
play(23,0,0). В этом модуле правило do_windows создает окна и выдает вспомогательную информацию. Правило play(23,0,0) фактически вы- полняет всю игру. Это правило вызывает другие модули и передает им исходное число 23. Правило play(M,H,C) построено из следующего за ним прави- ла, которое выполняет игровую последовательность до тех пор, пока не определится выигравший: play(M,H,C) :-
play_it_again(M,H,C). Переменные M, H и C используются соответственно для matches (спички), human (человек) и computer (компьютер). Правило play_it_again рекурсивное. Оно имеет три альтерна- тивы, ревализующие три возможных окончания процесса игры: выиг- рывает человек, выигрывает компьютер, игра продолжается. Если выигрывает человек, то правило имеет вид: play_it_again (M,H,C) :-
M <= 0,
nl, write(" Ты выиграл, человек!"),nl,
nl, write(" Нажмите клавишу пробела."),
readchar(_),
clearwindow, !. Предикат равенства M<=0 определяет, что компьютер сделал последний ход. Сделав это, он взял все спички (M=0), или же он взял больше спичек, чем фактически имеется (M<0). В том и дру- гом случае компьютер проигрывает, а человек выигрывает. Если выигрывает компьютер, то соответствующее правило име- ет вид: play_it_again (M,H,C) :-
M = 1,
nl, write(" Я, компьютер, выиграл !"),nl,
readchar(_),
clerawindow, !. В этом варианте правила предикат равенства M = 1 определя- ет, что текущее число спичек равно 1, и что эта последняя спич- ка должна быть удалена человеком во время следующего хода, т.е. есть компьютер выигрывает. Как первый, так и второй варианты правила play_it_again выполняют выход из программы, если они успешны. Третий вариант правила является сердцевиной игровой после- довательности: play_it_again(M,H,C) :-
nl,write(" Ваш ход."),
nl,write(" Сколько спичек вы хотите убрать?"),
readint(Hn),
M2 = M - Hn,
H2 = Hn,
write(" Теперь имеется",M2,"спичек"),
nl,nl,write(" Мой ход."),
nl,write(" Я думаю !"),
random(F),
Rea = 1 + 3 * F,
real_int(Rea,Rint),
M3 = M2 - Rint,
nl,write(" Я удалил ",Rint,"."),
nl,write(" Теперь имеется ",M3,"спичек"),nl,
M7 = M3,
H7 = H2,
C7 = Rint,
play_it_again(M7,H7,C7). Этот вариант правила считывает ответ введенный пользовате- лем, вычисляет число оставшихся спичек и выдает это число на экран. Затем правило считывает ввод компьютера (случайное целое число, равное 1, 2 или 3), модифицирует число спичек и выдает измененное значение. Этот третий вариант правила является ре- курсивным правилом, так как это необходимо для play_it_again. Описанные правила используются в программе для простой иг- ры в 23 спички (листинг 12.2). Эта программа использует средст- ва оконного интерфейса Турбо-Пролога и выдает вспомогательные сообщения и приглашения. Листинг 12.1 На рис. 12.3 показан диалог между пользователем и програм- мой. Это представление на экране диалога между компьютером и пользователем сделано достаточно просто и ясно, так что пользо- ватель может легко проанализировать ситуацию и решить какое ввести число. Пользователь может использовать свою собствееную выигрышную стратегию. Компьютер выбирает 1, 2 или 3 случайным образом - он не использует никакой стратегии, кроме надежды на удачу. Поэтому пользователь имеет больше шансов выиграть, чем компьютер. Рисунок 12.3. * Упражнения 12.5. Запустите программу для простой игры в 23 спички. Сколько раз вам удалось выиграть? 12.6. Предположим, что вы хотите реализовать вариант игры с че- тырьмя кучками. Напишите последовательность шагов для процедуры игры. (Подсказка: необходимо начать с четырех кучек спичек, ге- нерировать больше случайных чисел и модифицировать во время иг- ры все кучки). 12.7. Модифицируйте программу, используя процедуру, разрабо- танную в предыдущем упражнении. Запустите программу и проконт- ролируйте поведение как компьютера, так и пользователя. 12.3.3. Интеллектуальная игра "23 спички" В простой игре "23 спички" компьютер как игрок был запрог- раммирован на выбор числа удаляемых спичек случайным образом. Компьютер играл не имея выигрышной стратегии. Программа интел- лектуальной игры "23 спички" показывает, как реализовать выиг- рышную стратегию (или эвристику) для компьютера. В игре "23 спички", как вы понимаете, каждый игрок стара- ется оставить одну спичку другому игроку, чтобы он ее убрал. Начальное число спичек 23, конечная цель - одна спичка, а каж- дый игрок может удалить 1, 2 или 3 спички. Таким образом, можно увидеть последовательности начальных, промежуточных и конечных состояний кучки с точки зрения каждого игрока. Для того чтобы продемонстрировать ситуации, возникающие в процессе игры в табл. 12.2 показаны игровые последовательности. Таблица 12.2. Игровые последовательности для игры в 23 спички ________________________________________________________________ 1Число спичек, Остающееся Число спичек, Остающееся Комментарии 1удаляемое число удаляемое число 1пользователем спичек компьютером спичек Последова- 3 20
3 17 тельность 3 14
1 13 # 1 3 10
1 9
3 6
1 5
1 4
3 1 Компьютер
выигрывает Последова- 1 22
1 21 тельность 2 19
2 17 # 2 3 14
1 13
2 11
2 9
1 8
3 5
2 3
2 1 Компьютер
выигрывает ________________________________________________________________ Для первой игры эти последовательности следующие: S = [23,20,17,14,13,10,9,6,5,4,1] S = [23,17,13,9,5,1] - оставлены пользователю S = [20,14,10,6,4] - оставлены компьютеру Для второй игры эти последовательности следующие: S = [23,22,21,19,17,14,13,9,8,5,3,2,1] S = [23,21,17,13,9,5,1] - оставлены пользователю S = [22,19,14,11,8,3] - оставлены компьютеру Анализ показывает, что некоторые числа в последовательнос- ти нежелательны, если они оставляются вам. Эти числа: 2,3,4,6,7 и 8. С другой стороны, числа 1, 5 и 9 можно спокойно оставить противнику. Заметьте, что в последовательностях, показанных для этих двух игр, обе последовательности пользователя содержат "опас- ные" числа. Первая последовательность для компьютера содержит 6 и 4, и оба эти числа "безопасные". Этот анализ также показывает, что каждый игрок пытается оставить "опасные" числа противнику. Поэтому целесообразно най- ти соотношение между удаляемым числом спичек и "опасным" чис- лом, оставляемым противнику. Энтузиасты и теоретики изучили этот аспект игры и предложили различные решения. Упрощенная версия решения приводится ниже. Если M - число остающихся спичек, то надо удалить C спичек. Величина C вычисляется по формуле: C = (R + 3) - 4 * integer((R + 3)/ 4), где R = M - (4 * integer(M/4)). Здесь integer подразумевает целую часть значения выраже- ния в скобках. Например, если M = 8, то C = 3; если M = 6, то C = 1. Эти формулы могут быть записаны как правила Турбо-Пролога. Последовательность их записи следующая: A_r = M2/4 - 0.45, real_int(A_r, A_int), A1_int = M2 - (4 * A_int), A2_r = (A1_int + 3)/4 - 0.45, real_int(A2_r, A2_int), A3_int = (A1_int + 3) - (4 * A2_int), Эти правила присваивают A3_int целое значение числа спи- чек, которые должны быть удалены. (Правило real_int необходимо для преобразования действительного числа в целое.) Предложенная выигрышная стратегия может быть использована (в виде правил Турбо-Пролога) для замены простой случайной стратегии на основе случайного числа, которая использовалась в предыдущей программе игры в 23 спички. В результате получим программу интеллектуальной игры в 23 спички. Листинг 12.3. Интеллектуальная программа игры в 23 спички выдает число спичек в кучке. Затем она считывает ввод пользователя для опре- деления числа удаляемых спичек, модифицирует число спичек в кучке и выдает их новое число. Во время хода компьтера программа использует формулу выйг- рышной стратегии для вычисления числа удаляемых спичек. Затем программа удаляет это число из кучки и выдает новое их число. Игра повторяется до тех пор, пока один из игроков не выиг- рает. Затем объявляется победитель и звучит музыкальный сигнал. Заметьте, что программа имеет два дополнительных окна в правой половине экрана. Верхнее окно показывает модифицирован- ное число спичек после каждого хода. Нижнее окно демонстрирует победителя. На рис. 12.4 показан сеанс интеллектуальной игры в 23 спички. Заметьте, что компьютер выиграл. Рисунок 12.4. * Упражнения 12.8. Запустите интеллектуальную программу игры в 23 спички. Выиграли ли вы хотя бы один раз? Использовали ли вы ту же стра- тегию, что и компьютер, или какую-либо другую? 12.9. Модифицируйте выигрышную стратегию так, чтобы человек имел вероятность выиграть 40%. (Подсказка: необходимо модифици- ровать формулу или изобрести собственную стратегию.) 12.10. Запустите модифицированную программу и сыграйте несколь- ко партий. 12.4. Задача "Обезьяна и бананы" Задача "Обезьяна и бананы" - это классическая задача ИИ, демонсрирующая хорошо известные методы вывода при программиро- вании на Турбо-Прологе. Задача сформулируется следующим обра- зом:
Обезьяна находится в запертой комнате. В комнате на
полу лежит коробка, а связка бананов свисает с потолка
так, что достать ее обезьяна не может. Решение задачи
- это последователь-
ность действий, которые позволят обезьяне взять бананы. Последовательность действий обезьяны может быть организо- вана следующим образом:
Обезьяна подходит к коробке, двигает коробку под бана-
ны.
Обезьяна залезает на коробку и хватает бананы. 12.4.1. Разработка программы. Программа, решающая задачу "Обезьяна и бананы", должна со- держать в базе данных некоторые конкретные факты. Ими являются местоположение обезьяны в комнате, местоположение коробки в комнате, позиция на полу комнаты, над которой висит связка ба- нанов и информация о том, что сидит обезьяна на коробке или нет. Для этой задачи местоположение бананов, коробки и обезьяны изменяются и может быть представлено переменными Турбо-Проло- га.. Причем место нахождения обезьяны и коробки может быть лю- бым. Предварительный анализ приводит к следующим начальным ус- ловиям: - бананы над позицией 1 - 10; - коробка в позиции 1 - 10; - обезьяна в позиции от 1 - 10; - обезьяна не на коробке; - у обезьяны нет бананов. Эти факты описывают начальное состояние для задачи. Если обезьяна преуспеет в своей деятельности, то конечное состояние для задачи будет описываться при помощи следующих фактов: - бананы в первоначальной позиции; - коробка в той же позиции, что и бананы; - обезьяна в той же позиции, что и бананы; - обезьяна на коробке; - обезьяна схватила бананы. Теперь необходимо построить правила трансформации исходно- го состояния в конечное. Ниже приводятся возможные форматы для этих правил: move_to(box,position,position). /* передвинуть */ move_to(monkey,position,position). /* передвинуть */ go_to(position,position,position). /* перейти */ push_box(position,position,position). /* переместить */
/* коробку */ climb_box.
/* взобраться на */
/* коробку */ grasp_bananas.
/* схватить
/* бананы */ Первое правило move_to предназначено для передвижения ко- робки в новое положение, а второе правило move_to предназначено для перемещения обезьяны в новое положение. Правило go_to пред- назначено для логического перемещения коробки или обезьяны из одной позиции в другую. Правило push_box предназначено для пе- ремещения обезьяной коробки из одного положения в другое. Пра- вило climb_box предназначено для перемещения обезьяны на короб- ку. Правило grasp_bananas предназначено для хватания бананов. 12.4.2. Программа "Обезьяна и бананы" Используя базу данных и правила, рассмотренные в предыду- щем разделе, можно написать программу на Турбо-Прологе, которая решает задачу об обезьяне и бананах. Блок-схема этой программы показана на рис.12.5. Основной модуль на этой блок_схеме - это solve_the_problem (решить задачу). Этот модуль вызывает подмо- дули request_positions (запросить позиции) и go_and_get_bananas (иди и возьми бананы). Первый из них приглашает пользователя ввести местоположения обезьяны, коробки и бананов. Эти коорди- наты присваиваются переменным M, Bo и B. Модуль monkey_works (обезьяна работает) вызывает четыре модуля: move_to(box,B,Bo), move_to(monkey,Bo,M), climb_box и grasp_bananas. Модуль move_to(box,B,Bo) вызывает модуль push_box(C,B), который в свою очередь вызывает модуль move_to(monkey,B,M). Последний модуль вызывает модуль go_to(M,B) для выполнения премещения обезьяны из одной позиции в другую. Модуль climb_box выполняет перемеще- ние обезьяны, а модуль grasp_bananas выполняет "хватание" бана- нов обезьяной. Рисунок 12.5. На листинге 12.4 приведена программа "Обезьяна и бананы", являщаяся реализацией рассмотренного проекта. Листинг 12.4. База данных "Обезьяна и бананы" состоит из следующих ут- верждений: monkey_is_off_box. is_at(bananas,1). is_at(bananas,2). is_at(bananas,3). is_at(bananas,4). is_at(bananas,5). is_at(bananas,6). is_at(bananas,7). is_at(bananas,8). is_at(bananas,9). is_at(bananas,10). is_at(box,1). is_at(box,2). is_at(box,3). is_at(box,4). is_at(box,5). is_at(box,6). is_at(box,7). is_at(box,8). is_at(box,9). is_at(box,10). is_at(monkey,1). is_at(monkey,2). is_at(monkey,3). is_at(monkey,4). is_at(monkey,5). is_at(monkey,6). is_at(monkey,7). is_at(monkey,8). is_at(monkey,9). is_at(monkey,10).
Первое утверждение - это простой факт: обезьяна не на ко- робке. Остальные утверждения отражают допустимое местоположение бананов, коробки и обезьяны. Целевой модуль имеет вид: solve_the_problem :-
make_window(1,7,7,"",0,0,25,80),
make_window(2,7,7," MONKEY AND BANANAS ",
1,10,23,50),
request_position(B,Bo,M),
nl,write(" Monkey is thinking. "),
nl,write(" 'I want those bananas"),
write(" hanging up there !'"), nl,
go_and_get_bananas(B,Bo,M),
write(" Monkey gets the bananas !"), nl,nl,
write(" THREE CHEERS !!!"), nl,nl,
write(" Press the SPACE BAR."),
readchar(_). Этот модуль выдает информационный текст в окно на экране. Затем он вызывает два модуля request_position и go_and_get_bananas. Второй из них выполняет "захват бананов", вызывая подмодуль monkey_works. Основной модуль выдает некото- рый заключительный текст. Правило monkey_works реализовано следующим образом. monkey_works(B,Bo,M) :-
nl, write(" Monkey looks around and spies the box. "),
move_to(monkey,Bo,M),
nl, write(" Monkey goes from ",M," to ",Bo,"."),nl,
move_to(box,B,Bo),
nl, write(" Monkey pushes the box from ",Bo,
" to ",B,"."),nl,
climb_box,
grasp_bananas. Основная задача этого модуля разбита на последовательность более мелких подзадач. Правила move_to и go_to совместно пере- водят обезьяну из одного положения в другое: move_to(monkey,B,M) :-
is_at(monkey,B),
is_at(monkey,M),
go_to(M,B),
fail. move_to(box,B,M),
is_at(box,B),
is_at(box,C),
push_box(C,B). go_to(B,C) :-
monkey_is_off_box,
retract(is_at(monkey,B)),
assert(is_at(monkey,C)),
nl,write(" Monkey goes ",B," to ",C,"."). Правило move_to проверяет допустимость позиций B и С. Пра- вило go_to фактически удаляет из базы данных старое положение обезьяны и добавляет туда ее текущее положение. Правила move_to и push_box совместно выполняют перемещение коробки из одного положения в другое: move_to(box,B,M) :-
is_at(box,B),
is_at(box,C),
push_box(C,B). push_box(B,C) :-
monkey_is_off_box,
move_to(monkey,B,M),
retract(is_at(monkey,B)),
retract(is_at(box,B)),
assert(is_at(monkey,C)),
assert(is_at(box,C)). Правило move_to проверяет, допустимость параметров B и C и передает эти параметры правилу push_box. Правило push_box вы- зывает правило move_to(monkey,B,M) для того, чтобы включить обезьяну в процесс передвижения коробки в новое положение. Пра- вило push_box удаляет из базы данных старые положения обезьяны и коробки и добавляет туда их новые положения. Если достигнуто конечное положение обезьяны и коробки, то правила climb_box и grasp_bananas заставляют обезьяну залезть на коробку и схватить бананы: climb_box :-
monkey_is_off_box,
retract(monkey_is_off_box),
write(" Monkey climbs the box."),nl. grasp_bananas :-
write(" Monkey grasp the bananas."),nl. Тело правила climb_box удаляет утверждение monkey_is_off_box, что соответствует тому, что обезьяна залеза- ет на коробку. Правило grasp_bananas не имеет параметров и ими- тирует хватание обезьяной бананов. На рис. 12.6 показан сеанс выполнения программы об обезь- яне и бананах. Содержимое экрана дает информацию о начальном положении обезьяны и коробки, а также позицию, над которой ви- сят бананы. Краткий текст сценария показывает действия обезьяны и работу правил программы. Задача "Обезьяна и бананы" решается при помощи специаль- ной системы представления местоположений предметов в пространс- тве, т.е. состояние системы (обезьяны, коробки и бананов) пред- ставляется в терминах положения обезьяны, коробки и бананов. Для этой задачи состояние определяется пятью параметрами: состояние(B,Bo,M,C,G), где B - положение бананов, Bo - положение коробки, M - положе- ние обезьяны, C определяет находится ли обезьяна на коробке, а G определяет схватила ли обезьяна бананы (0 - не схватила, 1 - схватила). Начальное состояние следующее: состояние(5,10,1,0,0), а конечное состояние имеет вид: состояние(5,5,5,1,1). Рисунок 12.6. Правила Турбо-Пролога в этой программе - это операторы, которые действуют над пространством состояний, преобразуя их в новые. Для данной задачи начальное состояние обрабатывается этими правилами с тем, чтобы перевести всю систему в конечное состояние. На рис. 12.7 показана диаграмма состояний для этой задачи. Метод, использованный в программе об обезьяне и бананах, является примером обратного вывода. Программа начинается с цели и движется назад при помощи подцелей, возникающих во время уни- фикации. В Прологе левая часть правила (голова правила) являет- ся целью. Правая часть правила (тело правила с подцелями) ис- пользуется для сопоставления с данными в базе данных. Если про- цессы сопоставления оказываются успешными, то левая часть пра- вила также успешна. По этой причине системы с обратным выводом называются системами, управляемыми целью. Экспертная система о собаках, основанная на правилах, с которой вы познакомились в главе 10, является примером экспертной системы с обратным выво- дом. * Упражнения 12.11. Модифицируйте программу "Обезьяна и бананы" так, чтобы обезьяне потребовалась палка для того, чтобы достать бананы. 12.5. Задача "Воссоединение семьи Смита" Последняя программа этой главы является одной из простых дедуктивных задач, для которых игрок поначалу кажется, что от- вет должен очевиден. Однако вск не иак просто, поскольку имею- щиеся данные представляют собой множество связанных фактов, ко- торые должны быть тщательно рассмотрены для получения решения. Мощные внутренние унификационные подпрограммы Турбо-Пролога яв- ляются основным средством, используемым в программе "Воссоеди- нение семьи Смита". 12.5.1. Формулировка задачи В качестве области применения логического решателя задач вообразим себе гипотетическое воссоединение семьи. Семья Смитов собралась впервые за несколько лет. Четыре сестры Смита вышли замуж. После непростой дискуссии они пришли к выводу, что их некоторые привычки и интересы могут позволить двум или более парам неплохо провести отпуск вместе. Классификация интересов и привычек пар следующая: 1. Диета. 2. Развлечения. 3. Место проведения отпуска. 4. Время подъема утром. Факты, касающиеся интересов и привычек пар, а также имена семейных пар хранятся в базе данных. Пользователь должен иден- тифицировать две пары, которые имеют общие интересы, исходя из заданной информации. 12.5.2. Разработка алгоритма программы Для решения этой логической головоломки необходимо создать базу данных и набор определенных правил. При разработке базы данных, вы можно ввести блок, в котором перечислены все семей- ные пары. Для этой цели подходит предикат базы данных, имеющий вид: married(string,string). Объектами такого предиката являются имена четырех сестер Смита и их мужей. Для описания склонностей каждой пары могут быть использованы следующие предикаты базы данных: diet(string, string). entertainment(string, string). location(string, string). rise_time(string, string). Объектами этих предикатов являются имена мужей и данные, которые ввявлены во время предыдущего обсуждения. Случай, когда пара оказывалась безразличной в отношении того или иного вопро- са, выражается двумя предикатами. Например, факт "Рон может есть любую пищу" приводит к следующим двум предикатам: diet("Ron","vegetarian"). diet("Ron", "non-vegetarian"). Эти предикаты используются при выполнении сопоставления. Для того чтобы помочь пользователю (игроку), программа со- держит меню, при помощи которого игрок он может просмотреть па- ры, их интересы и привычки. Для того чтобы написать правила просмотра пар и их интересов, необходимо некоторое число обра- батывающих правил. Например, одно из таких правил может сопос- тавить пары в соответствии с их вкусами, касающимися еды. Ос- новная часть тела этого правила должна содержать следующие пре- дикаты: process(1) :-
clear_window,
nl, write(" Turbo Prolog has searched "),
nl, write(" the list of dietary preferences"),
nl, write(" and marriage matches."),
nl, write(" This search shows that "),nl,
married(X1,Y1),
diet(X1,Y1),
nl, write(" ",X1," or ",Y1," or both "),
nl, write(" like ",D1," food."),
nl, write(" --Press the SPACE bar to continue."),
nl, readchar(_),
fail. В этом правиле X1 и Y1 - переменные обозначающие имена пар, а D1 - это переменная, обозначающая предпочитаемые парой блюда. Во время сопоставления X1 и Y1 получают значения имени мужа и жены для каждого предиката married, а D1 получает значе- ние, соответствующее блюду, которое по вкусу мужу. Предикат write выдает на экран информацию для игрока. Аналогичные прави- ла обработки имеются и для других склонностей из приведенного списка. Затем необходимо разработать серию правил для сопоставле- ния пар в соответствии с их общими интересами и привычками. Ис- пользуя второе меню игрок выбирает категорию, для которой будут выданы все возможные сопоставления. Правила для сопоставления пар и их интересов называются 1обрабатывающими 0 (process) прави- лами. Эти правила уже написаны. Основное тело такого правила для двух любых пар и названий предпочитаемых ими блюд содержит следующие предикаты: process(1) :-
clearwindow,
nl, write(" Turbo Prolog has searched the list"),
nl, write(" of dietary preferences and found"),
nl, write(" a match. It has also searched the"),
nl, write(" list of married people and put "),
nl, write(" husbands and wifes together."), nl,
nl, write(" The results are: "),
married(X1, Y1),
diet(X1, D1),
married(X2, Y2),
X1 <> X2,
diet(X2,D2),
D1 = D2,
nl, write(" ",X1," and ",Y1,", and ",X2," and ",Y2),
nl, write(" are possible matching couples "),
nl, write(" eating ",D1, " food."),nl,
nl, write(" --Press the SPACE bar to continue."),
nl, readchar(_),
fail. В этом правиле X1 и Y1 - переменные обозначающие имена первой пары из базы данных, а X2 и Y2 - переменные обозначающие имена второй пары из базы данных. Заметьте, что условие X1<>X2 необходимо для того, чтобы пара не была сопоставима сама с со- бой. Для начала процесса сопоставления, который выполняется здесь, переменным X1 и Y1 присвоены значения John и Mary, а D1 присвоено значение "vegetarian" (вегетарианец). Переменные X2 и Y2 имеют значения Sam и Jane, а D2 имеет значение "non-vegetarian" (невегетарианец). Так как "vegetarian" и "non-vegetarian" не сопоставимы, то это правило неуспешно. Турбо-Пролог выполняет возврат по базе данных и означивает переменные X2 и Y2 значениями следующей пары Ron и Amy, а затем присваивает D2 значение "vegetarian", соответствующее Ron в предикате diet базы данных. Так как "vegetarian" сопоставимо с "vegetarian", то правило успешно, и три строчки с предикатом write выдают результат сопоставления на экран. Предикат read_char считывает введенный пользователем символ, а само пра- вило откатывает весь процесс до тех пор, пока все допустимые сопоставления не будут выданы. Аналогичные правила обработки имеются и для остальных интересов и привычек из спписка. После того, как игрок просмотрел все исходные данные и со- поставимые пары, он может потребовать демонстрацию решения, то есть сопоставимые пары, чьи интересы совместимы в наибольшей степени. В этом случае обработка включает множественный поиск по образцу. Тело правила аналогично правилам рассмотренным вы- ше, с той лишь разницей, что программа определяет действительно ли две пары X1 и Y1, и X2 и Y2 такие, что все их интересы сов- падают. Иными словами D1 = D2, E1 = E2, L1 = L2 и R1 = R2, где D обозначает предпочитаемые блюда, E - предпочитаемые развл ечения, L - предпочитаемые места проведения отпуска, а R - при- вычное время подъема утром. Если программа находит сопоставление, то на экран выдаются имена пар и остальные, характеризующие их данные. Последний предикат передает управление в первое меню. Игрок выходит из программы только через него. 12.5.3. Программа "Воссоединение семьи Смита" Предшествующий анализ приводит к программе, имеющей структуру, изображенную на рис.12.8. Главный модуль do_the_job вызывает модуль show_menu_1, ко- торой в свою очередьдля выдачи исходных данных вызывает модули начиная process(1), process(2), proces(3) и process(4). Модуль process(6) выполняет выход из программы. Модуль procees(5) вы- зывает подмодуль show_menu_2, который в свою очередь вызывает шесть обрабатывающих модулей для выполнения сопоставления. Ис- пользуя схему на рис.12.8 можно написать Турбо-Пролог-програм- му, которая очень хорошо обрабатывает логические соотношения головоломки. Структура программы достаточно специфична, но ее идея весьма гибка, и хорошо приспосабливается для широкого класса логических задач. Полностью программа "Воссоединение семьи Смита" показана на листинге 12.5. Рисунок 12.8. Листинг 12.5. После запуска программы "Воссоединение семьи Смита" на эк- ран выдается информационный текст, открывающий игру. Затем пользователь получает приглашение нажать клавишу пробела, и на экране появляются строчки диалога. Следующая стадия программы основана на меню, так что игрок выбирает данные для просмотра и сопоставления, которые нужно будет сделать. Вариант завершающей стадии игры также выбирается при помощи меню, т.е. осуществля- ется множественный поиск по образцу двух пар с совместимыми ин- тересами и привычками. Затем игрока приглашают вернуться в пер- вое меню, в котором уже можно выбрать опцию выхода из програм- мы. На примере программы "Воссоединения семьи Смита" хорошо видны возможности Турбо-Пролога для интеграции графики в виде совместного использования текста и окон, выбора при помощи меню и множественного поиска по образцу. Типичный диалог во время сеанса игры, приведенный на рис.12.9, демонстрирует это. Рисунок 12.9. Программа "Воосоединении семьи Смита" демонстрирует инте- ресную технологию программирования, в результате которой рожда- ется программа максимально доброжелательная к пользователю. Программа иллюстрирует собой прямой вывод. Вначале игры сопос- тавимые пары неизвестны. Однако все факты, впоследствии исполь- зуемые для анализа и поиска решения вопроса, уже храняться в базе данных. Левая часть правил, составляющих программу, или голова, определяет интересующую пользователя комбинацию фактов, которые должны быть сопоставлены с фактами базы данных. Процесс сопоставления не должен влиять на них. Однако в результате ра- боты правила могут возникнуть новые данные, которые могут быть добавлены к уже существующим. В случае, когда сопоставление проходит успешно, переменным, указанным в голове правила, прис- ваиваются конкретные эначения. Эти данные могут образовывать дополнительную информацию в базе данных. Поэтому системы с пря- мым выводом называются системами 1с управлением по данным 0. В следующем разделе рассматриваются вопросы пользователь- ского интерфейса программы "Воссоединения семьи Смита" на более глубоком уровне, так как это интересная часть проекта и процес- са выполнения программы. 12.5.6. Пользовательский интерфейс Задача пользовательского интерфейса - создать у пользова- теля чувство непринужденности. В общем случае интерфейсу поль- зователя присущи три функции. Первая из них, и наиболее очевид- ная, - это вывод на экран некоторого текста, вводящего пользо- вателя в курс дела. При этом у пользователя исчезнет чувство неуверенности, и возникнет знакомое ему ощущение, появляющиеся при чтении обячного рассказа. Текст на экране должен начинаться со слов, подобных, например, "Однажды ...". Окна создаются при помощи предиката makewindow, а много- численные строки текста - предикатом write. Для наглядности часть текста смещается вправо. Этого добиваются введением слева желаемого числа пробелов в текст, заключенный в кавычки. Кавыч- ки начинают и завершают строку текста. За текстом, описывающим историю семтьи, выводится пояснительный текст (см. рис. 12.10). Дополнительные сведения выдаются пользователю по мере вы- полнения программы. Заметьте, что окно для второго сегмента текста как бы вложено в предыдущее. Однако, окна ограничивающие несколько сопоставимых элементов, таких как привычки и им по- добные, занимают в точности тоже место. Это показано на рис.12.11. На данном примере было продемонстрировано как использовать средства Турбо-Пролога и объединять графику, текст и команды программы в одном модуле. Вторая функция, возлагаемая на интерфейс - это обеспечить дружественный способ взаимодействия пользователя с базой дан- ных, являющейся основой любого решения, получаемогос помощью внутренних унификационных подпрограмм Турбо-Пролога. Разработ- чик представляет себе, что происходит на самом деле. Но нельзя считать, что каждый пользователь располагает той же степенью знаний. Он знает только то, что почерпнул из текста на экране. Таким образом, еще одна задача пользовательского интерфейса - это представить информацию, имеющуюся в базе данных, в логичес- ки очевидной и легко читаемой форме. Рисунок 12.10. Рисунок 12.11. Рисунок 12.12. Третья наиболее часто возлагаемая на пользовательский ин- терфейс функция - это обеспечение дружественного способа управ- ления программой. Это достигается при помощи приглашений, та- ких, например, как -- Press the SPACE BAR to continue.
(Нажмите пробел для продолжения) Эти приглашения позволяют пользователю читать и обдумывать информацию на экране столько времени, сколько это ему необходи- мо. В больших программах с помощью приглашения очень удобно пе- реходить от одного модуля к другому. В данном случае время не должно быть критичным. Более важной характеристикой программы здесь является удобство для пользователя. Без сомнения, фраза: "Когда Вы будете готовы, то нажмите "пробел" и вы увидите имена пар" значительно более легко воспринимаема, чем непосредственные ко- манды программы, такие как "Сопоставимые пары?" Выбор меню - это другой часто встречающийся дружественный пользователю способ управления программой. Этот прием использо- вался и ранее, но в программе о воссоединении семьи меню обес- печивают основу интерфейса с пользователем. Эти меню позволяют читателю не только работать с программой, но и видеть информа- цию, которая является весьма важной. Два меню в программе "Вос- соединение семьи Смита" обеспечивают обе названые функции - просмотр данных (с помощью первого меню) и просмотр сопоставле- ний ( с помощью второго меню). Оба меню показаны на рис.12.13. Рисунок 12.13. Имейте в виду, что вся информация, представленная здесь в форме игры, может быть полезна в других случаях, где следует дать возможность пользователю просмотреть данные до принятия решения. Средства организации меню Турбо-Пролога делают эту процедуру простой для программирования и эффективной во время использования. Модули пользовательского интерфейса хотя и удлинняют прог- рамму, но зато экономят время время пользователя и оберегают его от всяких непредвиденных ситуаций. Они придают программе черты более профессиональной. Во многих програмах пользователь- ский интерфейс становится критическим для успешной работы прог- раммы. * Упражнения 12.12. Модифицируйте программу "Воссоединения семьи Смита" так, чтобы в процессе сопоставления участвовали новые элементы, нап- ример, курение. Вам потребуется создать дополнительный набор предикатов, которые следует учесть в дополнительном диалоге. Незабудьте модифицировать также модуль process(6). (Возможно при этом может потребоваться изменить модуль process(5)).Можно упростить эту часть для удобства работы. Не забудьте, что со- поставление изменится, если данные будут образовывать новые комбинации. 12.13.Модифицируйте программу "Воссоединения семьи Смита", увеличив число пар до пяти и можете также уменьшить число пара- метров для сопоставления (например, исключить место отдыха). Но вновь сопоставление даст результат, определяемый комбинацией данных (успешные сопоставления могут не обнаружиться). При же- лании можете упростить программу, удалив модуль process(5). 12.7.Обзор главы Цель данной главы дать представление об основных концепци- ях решения задач в ИИ. Вы узнали как использовать в сложных программах многие из методов, описанные ранее. Вы участвовали в разработке и реализации таких игр как "Отгадай число" и "23 спички" (два варианта - простой и слож- ный). Во время реализации стало очевидно, что процедурно-ориен- тированное программирование естественно для них. Программы пот- ребовали сопоставления символов и разработки стратегий, которые и были с успехом реализованы на Турбо-Прологе. Вы также получили представление об основных понятиях и подходах к решению логических задач. Вы узнали, как были разра- ботаны и созданы такие, ставшие классическими, программы, как, например, "Обезьяна и бананы" и "Воссоединении семьи Смита". Вы увидели при выполнении упражнений насколько гибкими могут быть такие программы. Благодаря им стала очевидной вся мощь и гиб- кость Турбо-Пролога.
.
- 35 -
Названия рисунков главы 12 N Название рисунка 12.1. Сеанс игры с программой об отгадывании числа. 12.2. Структура для программы простой игры "23 спички". 12.3. Сенас игры с простой программой "23 спички". 12.4. Сеанс игры с интеллектуальной программой "23 спички". 12.5. Структурная схема программы "Обезьяна и бананы" 12.6. Сенас игры с программой "Обезьяна и бананы" 12.7. Диаграмма состояний для задачи "Обезьяна и бананы" 12.8. Структурная схема для программы "Воссоединении семьи Сми- та". 12.9. Диалог с программой "Воссоединение семьи Смита". 12.10. История семьи Смита 12.11. Дополнительная информация показана во вложенном окне. 12.12. Часть информации из базы данных программы "Воссоединение семьи Смита". 12.13. Различные меню для программы "Воссоединение семьи Смита".
Надписи на рисунках главы 12 N рисунка N надписи и ее текст 12.1. - 12.2. - 12.3. - 12.4. - 12.5. - 12.6. - 12.7. 1) Начальное состояние
2) Конечное состояние
3) (Состояние, соответствующее цели) 12.8. - 12.9. - 12.10. -
- 36 - 12.11. - 12.12. - 12.13. - .
- 37 -
Названия таблиц главы 12 N Название таблицы 12.1 В тексте 12.2 В тексте
Надписи к таблицам главы 12 N таблицы N надписи и ее текст 12.1 В тексте 12.2 В тексте .
- 38 -
Листинги программ главы 12
1Листинг 12.1. _________________________________________________________ /* Программа: Отгалай число Файл: PROG1201.PRO */ /* Назначение: Демонстрация простого варианта игры в */ /* "Отгадай число"
*/ /* Указание: Запустите программу. Цель содержится в */ /* программе
*/ domains predicates
play_the_game
give_info
play_it
generate_rand(integer)
play_it_sam(integer)
test_and_tell(integer,integer)
say_you_got_it_right(integer)
say_too_big
say_too_small goal
play_the_game. clauses
/* цель является правилом */
- 39 -
play_the_game :-
give_info,
play_it.
/* выдача информации пользователю об игре */
give_info :-
makewindow(1,7,7,"",0,0,25,80),
makewindow(2,7,7," A NUMBER Guessing Game",
2,20,22,45),
nl,write(" This is a number guessing game."),
nl,write(" I shall think of an integer number"),
nl,write(" between 1 and 100. You make a guess"),
nl,write(" and type in your guess. If your gues"),
nl,write(" is correct, I shall say so. If not, "),
nl,write(" I shall say your guess is too big or"),
nl,write(" too small."),nl,
nl,write(" When you are ready, press the space bar."),
nl,readchar(_),
clearwidow.
/* выполнение игры
*/
play_it :-
generate_rand(A),
play_it_sam(A).
/* генерация случайного числа */ generate_rand(X) :-
random(R),
X = 1 + R * 100,
nl,write(" I have thought of a number."),
nl,write(" Now, it is your turn !"),nl.
- 40 -
/* запрос к пользователю
*/
play_it_sam(X) :-
nl, write(" Type in your guess. "),
nl, readint(G),
test_and_tell(X,G),
play_it_sam(X).
/* проверка и выдача результата */
test_and_tell(X,G) :-
X = G,
say_you_got_it_right(X).
test_and_tell(X,G) :-
X > G,
say_too_big.
test_and_tell(X,G) :-
X < G,
say_too_small.
/* выдача сообщений
*/
say_too_big :-
nl,write(" Your guess is too big. "),
nl,write(" Try a smaller number. ").
say_too_small :-
nl,write(" Your guess is too small. "),
nl,write(" Try a bigger number. ").
say_you_got_it_right(X) :-
nl,write("You got it right."),
nl,write(" It is",X,"."),
- 41 -
nl,write(" Good bye !"),
nl,write(" Press the space bar."),
nl,readchar(_),
exit. /* конец программы
*/ _________________________________________________________ .
- 42 -
1Листинг 12.2. _________________________________________________________ /* Программа: Простой вариант Файл: PROG1202.PRO */ /* игры в "23 спички"
*/ /* Назначение: Демонстрация игры "23 спички" */ /* Указание: Запустите программу. Цель содержится в */ /* программе
*/ domains predicates
play_the_game
do_windows
play(integer,integer,integer)
play_it_again(integer,integer,integer)
real_int(real, integer) goal
play_the_game. clauses
/* цель является правилом
*/
play_the_game :-
do_windows,
play(23,0,0).
/* правило для образования окон */
do_windows :-
makewindow(1,7,7,"",0,0,25,80),
makewindow(2,7,7," Game of 23 Matches ",
- 43 -
1,5,22,40),
nl,write(" Welcome to the game of 23 Matches."),
M = 23,
H = 0,
C = 0,
nl,write(" There are ",M," matches to begin."),
nl,write(" We will take turns removing matches."),nl,
nl,write(" Each time you may remove 1, 2, or 3"),
nl,write( matches and then I'll do the same."),nl,
nl,write(" The player who has to remove"),
nl,write(" the last match will lose."),
nl,write(" To begin, there are 23 matches."),
nl,write(" The human has removed ",H," match."),
nl,write(" The computer has removed ",C," match."),nl.
play(M,H,C) :-
play_it_again(M,H,C).
/* правило для определения победителя */
play_it_again (M,_,_) :-
M <= 0,
nl, write(" You've won !"),nl,
nl, write(" Press the SPACE bar."),
readchar(_),
clerawindow, !,
exit.
play_it_again (M,_,_) :-
M = 1,
nl, write(" I, the computer, won !"),nl,
readchar(_),
clerawindow, !,
exit.
- 44 -
play_it_again(M,_,_) :-
nl,write(" Your turn."),
nl,write(" How many do you want to remove?"),
readint(Hn),
M2 = M2 - Hn,
H2 = Hn,
write(" Now there are ",M2," match(es)."),
nl,nl,write(" My turn."),
nl,write(" I am deciding !"),
random(F),
Rea = 1 + 3 * F,
real_int(Rea,Rint),
M3 = M2 - Rint,
nl,write(" I removed ",Rint,"."),
nl,write(" Now there are is(are) ",M3," match(es)."),nl,
M7 = M3,
H7 = H2,
C7 = Rint,
play_it_again(M7,H7,C7).
/* вспомогательное правило
*/
real_int(Re,In) :- Re = In. /* конец программы
*/ __________________________________________________________
.
- 45 -
1Листинг 12.3. __________________________________________________________ /* Программа: Интеллектуальный Файл: PROG1203.PRO */ /* вариант игры "23 спички"
*/ /* Назначение: Демонстрация игры в 23 спички */ /* Используется выигрышная стратегия */ /* Указание: Запустите программу. Цель содержится в */ /* программе
*/ domains predicates
play_the_game
do_windows
play(integer,integer,integer)
play_it_again(integer,integer,integer)
real_int(real, integer) goal
play_the_game. clauses
/* цель является правилом
*/
play_the_game :-
do_windows,
play(23,0,0).
/* правило для образования окон */
do_windows :-
makewindow(1,7,7,"",0,0,25,80),
- 46 -
makewindow(2,7,7," Game of 23 Matches ",
1,5,22,40),
makewindow(3,7,7," Observer ",1,50,10,20),
cursor(2,2),
write("RESULT UPDATE"),
gotowindow(2),
nl,write(" Welcome to the game of 23 Matches."),
M = 23,
H = 0,
C = 0,
nl,write(" There are ",M," matches to begin."),
nl,write(" The human has removed ",H," match."),
nl,write(" The computer has removed ",C," match."),nl.
/* правило для выполнения игры */
play(M,H,C) :-
play_it_again(M,H,C).
/* правило для выдачи сообщения, что выиг-*/
/* рал человек
*/
play_it_again (M,_,_) :-
M <= 0,
nl, write(" You the human won !"),nl,
makewindow(4,7,7,"Result ",13,50,10,15),
nl,write(" YOU WON !"),nl,nl,nl,
/* звуковой сигнал для пользователя */
sound(4,392),
sound(4,440),
sound(4,494),
sound(4,440),
sound(4,494),
sound(4,392),
nl, write(" Press the SPACE bar."),
- 47 -
readchar(_),
clerawindow, !,
exit.
/* правило для выдачи сообщения, что выиг-*/
/* рал компьютер
*/
play_it_again (M,_,_) :-
M = 1,
nl, write(" I, the computer, won !"),nl,
makewindow(5,7,7,"Result ",13,50,10,20),
nl,write(" I WON !"),nl,nl,nl,
/* звуковой сигнал для пользователя */
sound(4,394),
sound(4,440),
sound(4,494),
sound(4,440),
sound(4,494),
sound(12,392),
nl, write(" Press the SPACE bar."),
readchar(_),
clerawindow, !,
exit.
/* основное правило, выполняющее игру */
/* и повторяющееся несколько раз */
play_it_again(M,_,_) :-
nl,write(" Your turn."),
nl,write(" How many do you want to remove?"),
readint(Hn),
M2 = M2 - Hn,
H2 = Hn,
write(" Now there are ",M2," match(es)."),
gotowindow(3),
- 48 -
cursor(4,3),
write(M2, " match(es) "),
gotowindow(2),
nl,nl,write(" My turn."),
nl,write(" I am thinking !"),
A_r = M2/4 - 0.45,
real_int(A_r, A_int),
A1_int = M2 - (4 * A_int),
A2_r = (A1_int + 3)/4 - 0.45,
real_int(A2_r, A2_int),
A3_int = (A1_int + 3) - (4 * A2_int),
M3 = M2 - A3_int,
nl,write(" I removed ",A3_int,"."),nl,
nl,write(" Now there are is(are) ",M3," match(es)."),nl,
gotowindow(3),
cursor(4,3),
write(M3, " match(es) "),
gotowindow(2),
M7 = M3,
H7 = H2,
C7 = A3_int,
play_it_again(M7,H7,C7).
/* правило преобразования дейсвительного */
/* числа в целое
*/
real_int(Re,In) :- Re = In. /* конец программы
*/ __________________________________________________________ .
- 49 -
1Листинг 12.4. __________________________________________________________ /* Программа: Обезьяна и бананы PROG1204.PRO */ /* Назначение: Демонстрация решения головоломки о */ /* обезьяне и бананах
*/
domains database
is_at(symbol, integer)
monkey_is_off_box predicates
solve_the_problem
request_position(integer,integer,integer)
go_and_get_bananas(integer,integer,integer)
monkey_works(integer,integer,integer)
move_to(symbol,integer,integer)
go_to(integer,integer)
push_box(integer,integer)
climb_box
grasp_bananas goal
solve_the_problem clauses
/* база данных */
monkey_is_off_box.
- 50 -
is_at(bananas,1). is_at(bananas,2).
is_at(bananas,3). is_at(bananas,4).
is_at(bananas,5). is_at(bananas,6).
is_at(bananas,7). is_at(bananas,8).
is_at(bananas,9). is_at(bananas,10).
is_at(box,1). is_at(box,2).
is_at(box,3). is_at(box,4).
is_at(box,5). is_at(box,6).
is_at(box,7). is_at(box,8).
is_at(box,9). is_at(box,10).
is_at(monkey,1). is_at(monkey,2).
is_at(monkey,3). is_at(monkey,4).
is_at(monkey,5). is_at(monkey,6).
is_at(monkey,7). is_at(monkey,8).
is_at(monkey,9). is_at(monkey,10).
/* правила достижения бананов */
solve_the_problem :-
make_window(1,7,7,"",0,0,25,80),
make_window(2,7,7," MONKEY AND BANANAS ",
1,10,23,50),
request_position(B,Bo,M),
nl,write(" Monkey is thinking. "),
nl,write(" 'I want those bananas"),
write(" hanging up there !'"), nl,
go_and_get_bananas(B,Bo,M),
write(" Monkey gets the bananas !"), nl,nl,
write(" THREE CHEERS !!!"), nl,nl,
- 51 -
write(" Press the SPACE BAR."),
readchar(_).
request_position(B,Bo,M) :-
nl,nl,
write(" Enter position of the bananas(1-10). "),
readint(B),
write(" Enter position of box(1-10). "),
readint(Bo),
write(" Enter positin of monkey(1-10). "),
readint(M).
go_and_get_bananas(B,Bo,M) :-
is_at(bananas,B),
is_at(box,Bo),
is_at(monkey,M),
monkey_works(B,Bo,M).
monkey_works(B,Bo,M) :-
nl, write(" Monkey looks around and spies the box. "),
move_to(monkey,Bo,M),
nl, write(" Monkey goes from ",M," to ",B,"."),nl,
move_to(box,B,Bo),
nl, write(" Monkey pushes the box from ",Bo,
" to ",B,"."),nl,
climb_box,
grasp_bananans.
move_to(monkey,B,M) :-
is_at(monkey,B),
is_at(monkey,M),
go_to(M,B),
fail.
move_to(box,B,M),
- 52 -
is_at(box,B),
is_at(box,C),
push_box(C,B).
go_to(B,C) :-
monkey_is_off_box,
retract(is_at(monkey,B)),
assert(is_at(monkey,C)),
nl,write(" Monkey goes ",B," to ",C,".").
push_box(B,C) :-
monkey_is_off_box,
move_to(monkey,B,M),
retract(is_at(monkey,B)),
retract(is_at(box,B)),
assert(is_at(monkey,C)),
assert(is_at(box,C)).
climb_box :-
monkey_is_off_box,
retract(monkey_is_off_box),
write(" Monkey climbs the box."),nl.
grasp_bananans :-
write(" Monkey grasp the bananas."),nl. /* конец программы
*/ __________________________________________________________ .
- 53 -
1Листинг 12.5.
__________________________________________________________ /* Программа: Воссоединение Файл: PROG1205.PRO */ /* семьи Смита
*/ /* Назначение: Решение головоломки при помощи */ /* Турбо-Пролога
*/ /* Указание: Запустите программу. Цель содержится в */ /* программе
*/ domains
P, Q, R = integer database
married(string,string)
diet(string,string)
entertainment(string,string)
location(string,string)
rise_time(string,string) predicates
show_menu1
show_menu2
repeat
process(P)
proces(Q)
do_the_job
conversation
select_pairs(R)
match_rule(string,string)
goal
- 54 -
do_the_job. clauses
/* база данных
*/
/* объяления женатых пар
*/
married("John","Mary").
married("Sam", "Jane").
married("Ron", "Amy").
married("Bill","Alice").
/* информация о диетах
*/
diet("John","vegetarian").
diet("Sam", "non-vegetarian").
diet("Ron", "vegetarian").
diet("Ron", "non-vegetarian").
diet("Bill","vegetarian").
diet("Bill","non-vegetarian").
/* информация о развлечениях */
entertainment("John","movies").
entertainment("Sam", "movies").
entertainment("Ron", "movies").
entertainment("Ron", "dancing").
entertainment("Bill","dancing").
/* информация о месте проведения */
/* отдыха
*/
location("John","city").
location("John","country").
- 55 -
location("Sam", "country").
location("Ron", "country").
location("Bill","city").
/* информация о привычках */
rise_time("John","early").
rise_time("Sam", "late").
rise_time("Ron", "early").
rise_time("Bill","late").
/* правило повтора
*/
repeat.
repeat :- repeat.
/* цель является правилом */
do_the_job :- makewindow(1,7,7,"The Smith Family Reunion ",0,0,25,80), nl,write(" The four Smith sisters have all married. Returning to "), nl,write(" their parents' home for family reunion, the four "), nl,write(" couples discovered that they have several interests and"), nl,write(" habits in common. In fact, the girls thought that perhaps"), nl,write(" two or more of the couples had enough in common that they"), nl,write(" could spend their summer vacation together. "),nl, nl,write(" The married couples are: John and Mary; Sam and Jane"), nl,write(" Ron and Amy; and Bill and Alice."),nl, nl,write(" If you match the facts and interests each"), nl,write(" person reveals in casual conversation, you "), nl,write(" will know the couples that the best suited "), nl,write(" for a pleasant and rewarding vacation together. "), nl,write(" Each person speaks for both spouses as married "), nl,write(" people often do."), nl,
- 56 - nl,write(" The random snatches of conversation"), write(" the data and see the matches "), nl,write(" the conversation would imply. "), nl,write(" Finally, the matching couples will be shown."), nl,write(" --Press the SPACE BAR to continue."), nl,write(""), nl,write(""), readchar(_), clearwindow, conversation.
/* правило для демонстрации информацион- */
/* окна
*/ conversation :- makewindow(3,7,7," Conversation overhead ",2,4,20,68), nl,write(" These are the pertinent snatches of conversation "), nl,write(" that were overheard. "), nl, nl,write(" Mary prefers movies as entertainment;"), write(" she likes to rise erly."), nl,write(" Sam likes to vacation in the country;"), write(" he really prefers "), nl,write(" vegetarian food."), nl,write(" Ron would be happy with either movies"), write(" or dancing. He can eat any"), nl,write(" kind of food."), nl,write(" Alice is happy eating any kind of food;"), write(" exploring a new city is"), nl,write(" her favorite vacation."), nl,write(" John is not vegetarian and can be"), write(" happy vacationing in the"), nl,write(" city or in the country."), nl,write(" Jane likes movies as entertainment;"), write(" she is a late riser."),
- 57 - nl,write(" Amy rises early; she likes vacations"), write(" in the country."), nl,write(" Bill likes to go dancing frequently."), write(" He is late riser."), nl, nl,write(" --Press the SPACE BAR to continue. "), readchar(_), show_menu_1.
/* правило для демонстрации информаци- */
/* ционного окна
*/ show_menu1 :- repeat, makewindow(4,7,7," Information Window ",2,2,22,38),nl, write(" Enter one of the numbers shown"),nl, write(" to view the information below "),nl,nl, write(" 0. The married Couples. "),nl, write(" 1. Diet Interests. "),nl, write(" 2. Entertainment Interests."),nl, write(" 3. Vacation Location Interests."),nl, write(" 4. Rising habits. "),nl, write(" 5. to go to the Matching Menu."),nl,nl, write(" 6. to exit the program."),nl,nl, write(" Please enter choice: (0-6) "), readint(P), P < 7, process(P), P >= 7, !.
/* демонстрация женатых пар */
process(0) :- clearwindow, nl,write(" Married Couples "),nl,
- 58 - married(X1,Y1), nl,write(" ",X1," is married to ",Y1," ."), nl,write(" Press the SPACE BAR to continue."), nl, readchar(_), fail.
/* демонстрация диетологических привычек */ process(1) :- clearwindow, nl,write(" Turbo Prolog has searched "), nl,write(" the list of dietary preferences"), nl,write(" and marriage matches."), nl,write(" This search shows that "),nl, married(X1,Y1), diet(X1,D1), nl,write(" ",X1," or ",Y1," or both "), nl,write(" like ", D1," food."), nl,write(" --Press the SPACE BAR to continue."), nl, readchar(_), fail.
/* демонстрация предпочтения развлечений */ process(2) :- clearwindow, nl,write(" Turbo Prolog has searched the list"), nl,write(" of entertainment preferences and marriage matches."),nl, nl,write(" This search shows that "),nl, married(X1,Y1), entertainment(X1,E1), nl,write(" ",X1," or ",Y1," or both "), nl,write(" ","enjoy",E1," for entertainment."), nl,write(" --Press the SPACE BAR to continue."), nl, readchar(_),
- 59 - fail.
/* демонстрация предпочтений места отдыха */
process(3) :- clearwindow, nl,write(" Turbo Prolog has searched the list"), nl,write(" of vacation location preferences"), nl,write(" and marriage matches."),nl, nl,write(" This search shows that "),nl, married(X1,Y1), entertainment(X1,E1), nl,write(" ",X1," or ",Y1," or both like the"), nl,write(" ",L1," for a vacation location."), nl,write(" --Press the SPACE BAR to continue."), nl, readchar(_), fail.
/* демонстрация предпочтения времени */
/* подъма утром
*/
process(4) :- clearwindow, nl,write(" Turbo Prolog has searched the list"), nl,write(" of rasing habits and"), nl,write(" marriage matches."), nl,write(" This search shows that "),nl, married(X1,Y1), rise_time(X1,R1), nl,write(" ",X1," or ",Y1," or both like the"), nl,write(" like to rise ",R1,"."), nl,write(" --Press the SPACE BAR to continue."), nl, readchar(_), fail.
- 60 - process(5) :-
show_menu_2.
process(6) :- exit.
show_menu_2 :- makewindow(4,7,7," Preference Matching ",2,40,22,38), nl,write(" Enter one of the numbers below to"), nl,write(" view all matches for category"),nl,nl, nl,write(" 1. Diet Matches."), nl,write(" 2. Entertainment Matches."), nl,write(" 3. Vacation Location Matches."), nl,write(" 4. Rising Habit Matches. "), nl,write(" 5. You Match the Couples. "), nl,write(" 6. The most compatible couples."),nl, nl,write(" Please enter choice: (1-6) "), readint(Q), Q <= 6, proces(Q), Q > 6, !.
/* правило для сопоставления детологичес- */
/* кой информации
*/
proces(1) :-
clearwindow,
nl,write(" Turbo Prolog has serached the list"),
nl,write(" of dietary preferences and found "),
nl,write(" a match. It has also searched the "),
nl,write(" list of married people and put "),
nl,write(" husbands and wifes together."),nl,
nl,write(" The results are: "),
married(X1,Y1),
diet(X1,D1),
married(X2,D2),
- 61 -
X1<>X2,
diet(X2,D2),
D1 = D2,
nl,write(" ",X1," and ",Y1,", and ",X2," and ,Y2),
nl,write(" are possible matching couples "),
nl,write(" because both couples are happy"),
nl,write(" eating ",D1," food."),nl,
nl,write(" --Press the SPACE BAR to continue."),
nl, readchar(_),
fail.
/* правило для сопоставления информации о */
/* развлечениях
*/ proces(2) :-
clearwindow,
nl,write(" Turbo Prolog has serached the list"),
nl,write(" of entertainment preferences and"),
nl,write(" found a match. It has also searched"),
nl,write(" the list of married people and put "),
nl,write(" husbands and wifes together."), nl,
married(X1,Y1),
entertainment(X1,E1),
married(X2,D2),
X1<>X2,
entertainment(X2,E2),
E1=E2,
nl,write(" ",X1," and ",Y1,", and ",X2," and ,Y2),
nl,write(" are possible matching couples "),
nl,write(" because both couples are happy"),
nl,write(" with ",E1," for entertainment."),nl,
nl,write(" --Press the SPACE BAR to continue."),
nl, readchar(_),
fail.
- 62 -
/* правило для сопоставления информации о */
/* о месте проведения отдыха
*/
proces(3) :-
clearwindow,
nl,write(" Turbo Prolog has serached the list"),
nl,write(" of vacation preferences and"),
nl,write(" found a match. It has also searched"),
nl,write(" the list of married people and put "),
nl,write(" husbands and wifes together."),nl,
married(X1,Y1),
location(X1,L1),
married(X2,D2),
X1<>X2,
location(X2,L2),
L1=L2,
nl,write(" ",X1," and ",Y1,", and ",X2," and ,Y2),
nl,write(" are possible matching couples "),
nl,write(" because both couples like to "),
nl,write(" vacation in the ",L1,"."),nl,
nl,write(" --Press the SPACE BAR to continue."),
nl, readchar(_),
fail.
/* правило для сопоставления информации о */
/* времени подъема утром
*/
proces(4) :-
clearwindow,
nl,write(" Turbo Prolog has serached the list"),
nl,write(" of rise-time preferences and"),
nl,write(" found a match. It has also searched"),
nl,write(" the list of married people and put "),
nl,write(" husbands and wifes together."),nl,
- 63 -
married(X1,Y1),
rise_time(X1,R1),
married(X2,D2),
X1<>X2,
rise_time(X2,R2),
R1=R2,
nl,write(" ",X1," and ",Y1,", and ",X2," and ,Y2),
nl,write(" are possible matching couples "),
nl,write(" because both couples like to "),
nl,write(" rise ",R1,"."),nl,
nl,write(" --Press the SPACE BAR to continue."),
nl, readchar(_),
fail.
/* правило для сопоставления двух пар */
proces(5) :- clearwindow, makewindow(14,7,7," You Match Couples ",0,0,25,80), nl,write(" Now, remember that the aim is to match the couples"), nl,write(" whose interests are most compatible. You may take as"), nl,write(" much time as you need. When you ready, enter the"), nl,write(" number for the couples you think match best."), nl,write(" 1) John and Mary & Sam and Jane "), nl,write(" 2) John and Mary & Ron and Amy "), nl,write(" 3) John and Mary & Bill and Alice "), nl,write(" 4) Sam and Jane & Ron and Amy "), nl,write(" 5) Sam and Jane & Bill and Alice "), nl,write(" 6) Ron and Amy & Bill and Alice "), nl,write(" Enter the number for couples and press ENTER."), readint(R), R < 7, select_pairs(R), R >= 7, !.
- 64 - proces(6) :- clearwindow, makewindow(15,7,7," You Match Couples ",0,0,25,80), married(X1,Y1), diet(X1,D1), entertainment(X1,E1), location(X1,L1), rise_time(X1,R1), married(X2,Y2), X1 <> X2, diet(X2,D2), entertainment(X2,E2), location(X2,L2), rise_time(X2,R2), D1 = D2, E1 = E2, L1 = L2, R1 = R2, nl,nl, nl, write(" ",X1," and ",Y1,", and "X2),
write(" and ",Y2," would be happy on a"), nl, write(" vacations because"), nl, write(" both couples are happy eating "),
write(" ",D1," food,"), nl, write(" both couples are happy with"),
write(" ",E1," for entertainment,"), nl, write(" both couples like to"),
write(" vacation in the ",L1,", and"), nl, write(" both couples like to"),
write(" rise ",R1,"."), !, nl,nl, write(" Press the SPACE BAR."), readchar(_),
- 65 - clearwindow, show_menu_1. select_pairs(1) :- X1 = "John", X2 = "Sam", match_rule(X1,X2). select_pairs(1) :- nl,write(" Incomplete match results!"), nl,write(" Press the SPACE BAR."), readchar(_). select_pairs(2) :- X1 = "John", X2 = "Ron", match_rule(X1,X2). select_pairs(3) :- X1 = "John", X2 = "Bill", match_rule(X1,X2).
select_pairs(3) :- nl,write(" Incomplete match results!"), nl,write(" Press the SPACE BAR."), readchar(_). select_pairs(4) :- X1 = "Sam", X2 = "Ron", match_rule(X1,X2). select_pairs(4) :- nl,write(" Incomplete match results!"), nl,write(" Press the SPACE BAR."), readchar(_). select_pairs(5) :-
- 66 - X1 = "Sam", X2 = "Bill", match_rule(X1,X2). select_pairs(5) :- nl,write(" Incomplete match results!"), nl,write(" Press the SPACE BAR."), readchar(_). select_pairs(6) :- X1 = "Ron", X2 = "Bill", match_rule(X1,X2). select_pairs(6) :- nl,write(" Incomplete match results!"), nl,write(" Press the SPACE BAR."), readchar(_). match_rule(X1,X2) :- married(X1,Y1), diet(X1,D1), entertainment(X1,E1), location(X1,L1), rise_time(X1,R1), married(X2,Y2), diet(X2,D2), entertainment(X2,E2), location(X2,L2), rise_time(X2,R2), D1 = D2, nl,nl,nl, nl,write(" ",X1," and ",Y1,", and ",X2),
write(" and ",Y2," would be happy on a"), nl,write(" vacation to the extent that"), nl,write(" both couples are happy eating "),
write(" ",D1," food."),
- 67 - E1 = E2, nl,write(" Both couples are happy with"),
write(" ",E1," for entertainment."), L1 = L2, nl,write(" Both couples like to"),
write(" vacation in the ",L1,"."), R1 = R2, nl,write(" Both couples like to"),
write(" rise ",R1,"."), !, nl,write(" Press the SPACE BAR."), readchar(_). /* конец программы
*/ __________________________________________________________