Читаем Учебник по Haskell полностью

Игра перемешивается согласно правилам, поэтому функцию shuffle мы поселим в модуле Game. А функ-

ция readInt это скорее элемент взаимодействия с пользователем, ведь в ней мы распознаём число в строчном

ответе, она останется в модуле Loop.

Проверим работает ли наша программа:

*Loop> :r

[1 of 2] Compiling Game

( Game. hs, interpreted )

[2 of 2] Compiling Loop

( Loop. hs, interpreted )

Ok, modules loaded: Game, Loop.

*Loop>

Работает! Можно спускаться по дереву выражения ниже. Сейчас нам предстоит написать одну из самых

сложных функций, это функция gameLoop.

13.2 Пятнашки

Цикл игры

Функция цикла игры принимает текущую позицию. При этом у нас два варианта. Возможно игра пришла

в конечное положение (isGameOver) и мы можем сообщить игроку о том, что он победил (showResults), если

это не так, то мы покажем текущее положение (showGame), спросим ход (askForMove) и среагируем на ход

(reactOnMove).

-- в модуль Loop

gameLoop :: Game -> IO ()

gameLoop game

| isGameOver game

= showResults game >> setup >>= gameLoop

| otherwise

= showGame game >> askForMove >>= reactOnMove game

showResults :: Game -> IO ()

showResults = un

showGame :: Game -> IO ()

showGame = un

Пятнашки | 205

askForMove :: IO Query

askForMove = un

reactOnMove :: Game -> Query -> IO ()

reactOnMove = un

-- в модуль Game

isGameOver :: Game -> Bool

isGameOver = un

Как определить закончилась игра или нет это скорее дело модуля Game. Все остальные функции принадле-

жат модулю Loop. Функция askForMove возвращает реплику пользователя и тут же направляет её в функцию

reactOnMove. Функции showGame и showResults ничего не возвращают, они только меняют состояния экрана.

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

Обратите внимание на то, как даже не дав определение функции, мы всё же очерчиваем её смысл в

объявлении типа. Так посмотрев на функцию askForMove и сопоставив тип с именем, мы можем понять, что

эта функция предназначена для запроса значения типа Query, для запроса реплики пользователя. А по типу

функции showGame мы можем понять, что она проводит какой-то побочный эффект, судя по имени что-то

показывает, из типа видно что показывает значение типа Game или текущую позицию.

Отображение позиции

Определим функции отображения результата и позиции. Когда игра закончится мы покажем итоговое

положение и объявим результат.

showResults :: Game -> IO ()

showResults g = showGame g >> putStrLn ”Игра окончена.”

Теперь определим функцию showGame. Если тип Game является экземпляром класса Show, то определение

окажется совсем простым:

-- в модуль Loop

showGame :: Game -> IO ()

showGame = putStrLn . show

-- в модуль Game

instance Show Game where

show = un

Реакция на реплики пользователя

Теперь нужно определить функции askForMove и reactOnMove. Первая функция требует установить про-

токол реплик пользователя, в каком виде он будет набирать значения типа Query. Нам пока лень об этом

думать и мы перейдём к функции reactOnMove. Вспомним её тип:

reactOnMove :: Game -> Query -> IO ()

Функция принимает текущее положение и запрос пользователя. И ничего не возвращает, она продолжает

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

можно написать:

reactOnMove :: Game -> Query -> IO ()

reactOnMove game query = case query of

Quit

->

NewGame n

->

Play

m

->

Рассмотрим каждый из случаев. В первом случае пользователь говорит, что ему надоело и он уже наиг-

рался. Чтож попрощаемся и вернём значение единичного типа.

206 | Глава 13: Поиграем

...

Quit

-> quit

...

quit :: IO ()

quit = putStrLn ”До встречи.” >> return ()

В следующем варианте пользователь хочет начать всё заново. Так начнём!

NewGame n

-> gameLoop =<< shuffle n

Мы вызвали функцию перемешивания shuffle с заданным уровнем сложности. И рекурсивно вызвали

цикл игры с новой позицией. Всё началось по новой. В третьей альтернативе пользователь делает ход, на это

мы должны обновить позицию запустить цикл игры с новым значением:

-- в модуль Loop

Play

m

-> gameLoop $ move m game

-- в модуль Game

move :: Move -> Game -> Game

move = un

Функция move обновляет согласно правилам текущую позицию. Соберём все определения вместе:

reactOnMove :: Game -> Query -> IO ()

reactOnMove game query = case query of

Quit

-> quit

NewGame n

-> gameLoop =<< shuffle n

Play

m

-> gameLoop $ move m game

Слушаем игрока

Теперь всё же вернёмся к функции askForMove, научимся слушать пользователя. Сначала мы скажем

какую-нибудь вводную фразу, предложение ходить (showAsk) затем запросим строку стандартной функцией

getLine, потом нам нужно будет распознать (parseQuery) в строке значение типа Query. Если распознать его

Перейти на страницу:

Похожие книги

1С: Бухгалтерия 8 с нуля
1С: Бухгалтерия 8 с нуля

Книга содержит полное описание приемов и методов работы с программой 1С:Бухгалтерия 8. Рассматривается автоматизация всех основных участков бухгалтерии: учет наличных и безналичных денежных средств, основных средств и НМА, прихода и расхода товарно-материальных ценностей, зарплаты, производства. Описано, как вводить исходные данные, заполнять справочники и каталоги, работать с первичными документами, проводить их по учету, формировать разнообразные отчеты, выводить данные на печать, настраивать программу и использовать ее сервисные функции. Каждый урок содержит подробное описание рассматриваемой темы с детальным разбором и иллюстрированием всех этапов.Для широкого круга пользователей.

Алексей Анатольевич Гладкий

Программирование, программы, базы данных / Программное обеспечение / Бухучет и аудит / Финансы и бизнес / Книги по IT / Словари и Энциклопедии
1С: Управление торговлей 8.2
1С: Управление торговлей 8.2

Современные торговые предприятия предлагают своим клиентам широчайший ассортимент товаров, который исчисляется тысячами и десятками тысяч наименований. Причем многие позиции могут реализовываться на разных условиях: предоплата, отсрочка платежи, скидка, наценка, объем партии, и т.д. Клиенты зачастую делятся на категории – VIP-клиент, обычный клиент, постоянный клиент, мелкооптовый клиент, и т.д. Товарные позиции могут комплектоваться и разукомплектовываться, многие товары подлежат обязательной сертификации и гигиеническим исследованиям, некондиционные позиции необходимо списывать, на складах периодически должна проводиться инвентаризация, каждая компания должна иметь свою маркетинговую политику и т.д., вообщем – современное торговое предприятие представляет живой организм, находящийся в постоянном движении.Очевидно, что вся эта кипучая деятельность требует автоматизации. Для решения этой задачи существуют специальные программные средства, и в этой книге мы познакомим вам с самым популярным продуктом, предназначенным для автоматизации деятельности торгового предприятия – «1С Управление торговлей», которое реализовано на новейшей технологической платформе версии 1С 8.2.

Алексей Анатольевич Гладкий

Финансы / Программирование, программы, базы данных