52700.fb2
водит к выигрышу. Поэтому мы будем перемешивать так: мы стартуем из начального положения и делаем
несколько ходов произвольным образом. Количество ходов определяет сложность игры:
shuffle :: Int -> IO Game
shuffle n = (iterate (shuffle1 =<< ) $ pure initGame) !! n
shuffle1 :: Game -> IO Game
shuffle1 = un
Функция shuffle1 перемешивает фишки один раз. С помощью функции iterate мы строим список рас-
становок, которые мы получаем на каждом шаге перемешивания. В самом конце мы выбираем из списка
n-тую позицию. Обратите внимание на то, что мы не можем просто написать:
iterate shuffle1 initGame
Так у нас не совпадут типы. Для функции iterate нужно чтобы вход и выход функции имели одинаковые
типы. Поэтому мы пользуемся в функции iterate методами классов Monad и Applicative (глава 6).
Теперь определим функцию shuffle1. Мы делаем ход в текущей позиции, который мы выбрали случай-
ным образом из списка доступных ходов. Выбором случайного элемента из списка, будет заниматься функция
randomElem, а функция nextMoves будет возвращать список доступных ходов для данного положения:
shuffle1 :: Game -> IO Game
shuffle1 g = flip move g <$> (randomElem $ nextMoves g)
randomElem :: [a] -> IO a
randomElem = un
nextMoves :: Game -> [Move]
nextMoves = un
Нам осталось определить всего две функции, и всё готово для игры. Определим выбор случайного эле-
мента из списка:
import System.Random
...
randomElem :: [a] -> IO a
randomElem xs = (xs !! ) <$> randomRIO (0, length xs - 1)
Мы генерируем случайное число в диапазоне индексов списка и затем извлекаем элемент. Теперь функ-
ция определения ходов в текущем положении:
nextMoves g = filter (within . moveEmptyTo . orient) allMoves
where moveEmptyTo v = shift v (emtyField g)
allMoves = [Up, Down, Left, Right]
Мы выполняем схожие операции с теми, что были в функции move. Мы фильтруем из списка всех ходов
те, что выводят пустую фишку за пределы доски.
212 | Глава 13: Поиграем
Отображение положения
Я немного поторопился, нам осталась ещё одна функция. Это отображение позиции. Я не буду подробно
останавливаться на теле функции, скажу лишь то, что она составляет строку так как это показано в коммен-
тарии к функции.
--
+----+----+----+----+
--
|
1 |
2 |
3 |
4 |
--
+----+----+----+----+