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

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

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

делах доски. Например если пустышка расположена в самом верху и мы хотим сделать ход Up (передвинуть

её ещё выше), то положение игры не должно измениться.

import Prelude hiding (Either(.. ))

newtype Vec = Vec (Int, Int)

move :: Move -> Game -> Game

move m (Game id board)

| within id’ = Game id’ $ board // updates

| otherwise

= Game id board

where id’ = shift (orient m) id

updates = [(id, board ! id’), (id’, emptyLabel)]

-- определение того, что индексы внутри доски

within :: Pos -> Bool

within (a, b) = p a && p b

where p x = x >= 0 && x <= 3

-- смещение положение по направдению

shift :: Vec -> Pos -> Pos

shift (Vec (va, vb)) (pa, pb) = (va + pa, vb + pb)

-- направление хода

orient :: Move -> Vec

orient m = Vec $ case m of

Up

-> (-1, 0)

Down

-> (1 , 0)

Left

-> (0 , -1)

Right

-> (0 , 1)

-- метка для пустой фишки

emptyLabel :: Label

emptyLabel = 15

Маленькие функции within, shift, orient, emptyLabel делают как раз то, что подписано в комментариях.

Думаю, что их определение не сложно понять. Но есть одна тонкость, поскольку в функции orient мы поль-

зуемся конструкторами Left и Right необходимо спрятать тип Either из Prelude. Мы ввели дополнительный

тип Vec для обозначения смещения, чтобы случайно не подставить вместо него индексы.

Разберёмся с функцией move. Сначала мы вычисляем положение фишки, которая пойдёт на пустое место

id’. Мы делаем это, сместив (shift) положение пустышки (id) по направлению хода (orient a).

Мы обновляем массив, который описывает доску с помощью специальной функции //. Посмотрим на её

тип:

Пятнашки | 211

(//) :: Ix i => Array i a -> [(i, a)] -> Array i a

Она принимает массив и список обновлений в этом массиве. Обновления представлены в виде пары

индекс-значение. В охранном выражении мы проверяем, если индекс перемещаемой фишки в пределах дос-

ки, то мы возвращаем новое положение, в котором пустышка уже находится в положении id’ и массив об-

новлён. Мы составляем список обновлений updates bз двух элементов, это перемещения фишки и пустышки.

Если же фишка за пределами доски, то мы возвращаем исходное положение.

Перемешиваем фишки

Игра начинается с такого положения, в котором все фишки перемешаны. Но перемешивать фишки про-