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

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

извольным образом было бы не честно, поскольку известно, что в пятнашках половина расстановок не при-

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

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

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 |

--

+----+----+----+----+