52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 224

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 224

алгоритм перемешивания должен генерировать только такие позиции, для которых решение возможно.

Набросок решения

Программа, которую мы хотим написать, будет вести диалог с пользователем. Она показывает поле для

игры и спрашивает следующий ход. Потом она распознаёт ход, и показывает обновлённое поле. И так далее.

Нам нужно как-то организовать этот диалог.

При этом в программе можно выделить две независимые части. Одна отвечает за сам диалог. Она прини-

мает реплики пользователя и отображает поле для игры. А другая часть отвечает за правила игры пятнашки:

как ходы влияют на поле, какое положение является победным, как перемешивать фишки.

| 201

У нас будет два отдельных модуля: один для описания игры, назовём его Game, а другой для описания

диалога с пользователем. Мы назовём его Loop (петля или цикл), поскольку диалог это зацикленная проце-

дура получения реплики и реакции на реплику.

Такой вот набросок-ориентир. После этого можно приступать к реализации. Но с чего начать?

Каркас. Типы и классы

В Haskell программы обычно начинают строить с каркаса – с типов и классов. Нам нужно выделить ос-

новные сущности и подумать какие типы подходят для их описания лучше всего.

В нашей задаче есть поле с фишками и ходы. Мы делаем ходы и фишки двигаются. Поле – это матрица

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

ячейке массива хранятся фишки. Фишки обозначаются целыми числами:

type Pos

= (Int, Int)

type Label

= Int

type Board

= Array Pos Label

Пустую фишку мы будем также обозначать числом. Физически когда мы ходим, мы меняем положение

одной фишки. Но в нашем описании мы меняем местами две фишки, поскольку пустая фишка также обозна-

чается номером. Когда мы ходим, мы меняем положение пустой фишки, одним ходом мы можем сместить

её вверх, вниз, влево или вправо. Введём специальный тип для обозначения ходов:

data Move = Up | Down | Left | Right

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

Game будет содержать текущее положение пустой клетки и положение фишек:

data Game = Game {

emptyField

:: Pos,

gameBoard

:: Board }

Вот и все типы для описания игры. Сохраним их в модуле Game. Теперь подумаем о типах для диалога

с пользователем. В этом модуле наверняка будет много функций с типом IO, потому что в нём происходит

взаимодействие с игроком. Но, что является каркасом для диалога?

Если мы хотим с кем-нибудь общаться, необходимо чтобы у нас был с собеседником общий язык, он и

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

• Сделать ход

• Начать новую игру

• Выйти из игры

Если пользователь делает ход мы показываем новое положение поля, если он начинает новую игру мы

показываем ему новую перемешанную позицию, давайте у нас будет разная степень перемешанности фи-

гур. При перемешивании мы стартуем из победного положения и начинаем случайным образом делать хо-

ды. Чем больше ходов мы сделаем тем сложнее будет собрать игру. Поэтому пользователь будет указывать

число шагов для перемешивания при запросе новой игры. Если пользователь попросит закончить игру мы