52700.fb2
делах доски. Например если пустышка расположена в самом верху и мы хотим сделать ход 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з двух элементов, это перемещения фишки и пустышки.
Если же фишка за пределами доски, то мы возвращаем исходное положение.
Перемешиваем фишки
Игра начинается с такого положения, в котором все фишки перемешаны. Но перемешивать фишки про-