Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Лабы_ЭкспСист / Лабораторная №8

.doc
Скачиваний:
10
Добавлен:
09.02.2015
Размер:
57.86 Кб
Скачать

Лабораторная работа №8.

Интеллектуальные игры.

Цель работы: Изучить определение типа данных для Visual Prolog.

Задание: Написать программу для игры в Ним.

Состояние в игре Ним может быть представлено как набор кучек спичек; мы будем отображать каждую кучку как список целых чисел.

[1, 1, 1, 1, 1]

Следовательно, множество кучек будет списком списков; например,

[ [1,1,1,1,1],

[1, 1],

[1,1,1,1] ]

Два игрока, вы и компьютер, по очереди делаете ходы. Как только игрок не сможет сделать ход, игра заканчивается, и этот игрок проигрывает. Ход состоит из взятия как минимум одной спички из ровно одной кучки. В примере, если вы возьмёте три спички из третьей кучки, вы сделаете допустимый ход, и доска станет

[ [1,1,1,1,1],

[1, 1],

[1] ]

где вы убрали три спички из третьей кучки. Чтобы реализовать этот проект, мы будем использовать технику программирования, называемую incremental development of systems – наращивающая разработка систем. Сначала, вы реализуете и протестируете программу, которая присоединяет два списка. Так как вы собираетесь использовать эту программу и для разделения, и для конкатенации списков, вам нужно протестировать обе возможности. Фактически, если вы запустите первую программу, из указанных (ниже), вы получите

[[1],[1],[1],[1],[1],[1],[1],[1]]

[][[1],[1],[1,1]]

[[1]][[1],[1,1]]

[[1],[1]][[1,1]]

[[1],[1],[1,1]][]

Первая строка является результатом конкатенации двух множеств кучек; последующие строки показывают все возможности разделения списка кучек. Тогда первая программа, похоже, работает верно.

implement nimgame

open core

domains

ms= integer*.

hs= ms*.

class predicates

append:(E*, E*, E*) nondeterm anyFlow.

clauses

classInfo( "nimgame", "1.0").

append([], Y, Y).

append([U|X], Y, [U|Z]) :- append(X, Y, Z).

clauses

run():-

console::init(),

append([[1],[1],[1],[1],[1]], [[1],[1],[1]], L), !,

stdio::write(L), stdio::nl; succeed().

end implement nimgame

goal

mainExe::run(nimgame::run).

Следующий шаг – написать предикат, который забирает как минимум одну спичку из кучки; конечно, он может забрать больше, чем одну спичку. После удаления одной или более спичек из кучки, takesome/3 вставляет измененную кучку во множество кучек. Если вы протестируете программу, она напечатает следующий результат:

[[1,1,1,1],[1,1]]

[[1,1,1],[1,1]]

[[1,1],[1,1]]

[[1],[1,1]]

[[1,1]]

NB: первое предложение takesome/3 заверяет, что предикат не вставит пустую кучку во множество кучек.

implement nimgame

open core

domains

ms= integer*.

hs= ms*.

class predicates

append:(E*, E*, E*) nondeterm anyFlow.

test:() procedure.

takesome:(ms, hs, hs) nondeterm anyFlow.

clauses

classInfo( "nimgame", "1.0").

append([], Y, Y).

append([U|X], Y, [U|Z]) :- append(X, Y, Z).

takesome([_X], V, V).

takesome([_X, X1|Y], V, [[X1|Y]|V]).

takesome([_X|T], V, Y) :- takesome(T, V, Y).

test() :- L= [1, 1, 1, 1, 1],

V= [[1,1]],

takesome(L, V, Resp),

stdio::write(Resp), stdio::nl,

fail; succeed().

clauses

run():-

console::init(),

test().

end implement nimgame

goal

mainExe::run(nimgame::run).

Теперь вы готовы создавать все возможные ходы с заданной позиции. Если вы запустите тестовую программу, она выдаст следующий экран:

Possible moves from [[1,1,1],[1,1]]:

[[1,1],[1,1]]

[[1],[1,1]]

[[1,1]]

[[1,1,1],[1]]

[[1,1,1]]

implement nimgame

open core

domains

ms= integer*.

hs= ms*.

class predicates

append:(E*, E*, E*) nondeterm anyFlow.

takesome:(ms, hs, hs) nondeterm anyFlow.

move:(hs, hs) nondeterm anyFlow.

clauses

classInfo( "nimgame", "1.0").

append([], Y, Y).

append([U|X], Y, [U|Z]) :- append(X, Y, Z).

takesome([_X], V, V).

takesome([_X, X1|Y], V, [[X1|Y]|V]).

takesome([_X|T], V, Y) :- takesome(T, V, Y).

move(X, Y) :- append(U, [X1|V], X),

takesome(X1, V, R), append(U, R, Y).

run():- console::init(), L= [[1, 1, 1], [1, 1]],

stdio::write("Possible moves from ", L, ": "),

stdio::nl, move(L, Resp),

stdio::write(Resp), stdio::nl, fail; succeed().

end implement nimgame

goal mainExe::run(nimgame::run).

implement nimgame

open core

domains

ms= integer*.

hs= ms*.

class predicates

append:(E*, E*, E*) nondeterm anyFlow.

takesome:(ms, hs, hs) nondeterm anyFlow.

move:(hs, hs) nondeterm anyFlow.

winningMove:(hs, hs) nondeterm (i, o).

clauses

classInfo( "nimgame", "1.0").

append([], Y, Y).

append([U|X], Y, [U|Z]) :- append(X, Y, Z).

takesome([_X], V, V).

takesome([_X, X1|Y], V, [[X1|Y]|V]).

takesome([_X|T], V, Y) :- takesome(T, V, Y).

move(X, Y) :- append(U, [X1|V], X),

takesome(X1, V, R), append(U, R, Y).

winningMove(X, Y) :- move(X, Y),

not(winningMove(Y, _)).

run():- console::init(), L= [[1, 1, 1], [1, 1]],

winningMove(L, S),

stdio::write("Winning move: ", S),

stdio::nl, fail; succeed().

end implement nimgame

goal mainExe::run(nimgame::run).

Сердце этой программы – предикат

winningMove(X, Y) :- move(X, Y), not(winningMove(Y, _)).

Если есть выигрышная стратегия, этот потрясающий предикат в одну строчку найдёт её!

Ниже вы найдёте пример игры против компьютера. Как вы можете видеть, если есть шанс выиграть, машина возьмёт его.

[[1,1,1],[1,1]]

Your move: [[1], [1,1]]

My move: [[1],[1]]

Your move: [[1]]

My move: []

I win

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

% File: nimgame.pro

implement nimgame

open core, stdio

clauses

classInfo( "nimgame", "1.0").

run():- console::init(), L= [[1, 1, 1], [1, 1]],

write(L), nl, nim::game(L), ! ; succeed().

end implement nimgame

goal mainExe::run(nimgame::run).

Интерфейс класса экспортирует метод game/1 и домены, которые вам нужны для описания позиции в игре. Он определён ниже.

% File: nim.cl

class nim

open core

domains

ms= integer*.

hs= ms*.

predicates

classInfo : core::classInfo.

game:(hs) determ (i).

end class nim

% File:nim.pro

implement nim

open core, stdio

class predicates

append:(E*, E*, E*) nondeterm anyFlow.

takesome:(ms, hs, hs) nondeterm anyFlow.

move:(hs, hs) nondeterm anyFlow.

winningMove:(hs, hs) nondeterm (i, o).

iwin:(hs) determ (i).

youwin:(hs, hs) determ (i, o).

clauses

classInfo("nim", "1.0").

append([], Y, Y).

append([U|X], Y, [U|Z]) :- append(X, Y, Z).

takesome([_X], V, V).

takesome([_X, X1|Y], V, [[X1|Y]|V]).

takesome([_X|T], V, Y) :- takesome(T, V, Y).

move(X, Y) :- append(U, [X1|V], X),

takesome(X1, V, R), append(U, R, Y).

winningMove(X, Y) :- move(X, Y), not(winningMove(Y, _)).

iwin([]) :- !, write("I win"), nl.

youwin(L, L2) :- winningMove(L, L2), !.

youwin(_L, _L2) :- write("You win"), nl, fail.

game(L1) :- write("Your move: "),

L= console::read(), move(L1, LTest),

L= LTest, youwin(L, L2), !,

write("My move: "), write(L2), nl,

not(iwin(L2)), game(L2).

end implement nim

Стратегия, использованная в этой очень простой программе, называется алгоритмом минимакса (minmax algorithm). Алгоритм может быть применён к любой игре с двумя соперниками, где оба имеют совершенно точное знание о состоянии игры. Примеры таких игр: шахматы, Го, шашки, крестики-нолики и Ним. Он также может быть использован для контролирования роботов.

Соседние файлы в папке Лабы_ЭкспСист