knight.hs (2523B)
1 {-# OPTIONS_GHC -Wall #-} 2 {-# LANGUAGE BangPatterns #-} 3 4 module Main where 5 6 import Prelude hiding (Monad, return, (>>=)) 7 import qualified Data.HashSet as HashSet 8 import Data.HashSet (HashSet, fromList, empty, singleton) 9 import Data.Hashable (Hashable) 10 11 -- | Standard monad class with Hashable/Eq constraints on parameters. 12 class HashableMonad m where 13 return :: Hashable a => a -> m a 14 (>>=) :: (Hashable a, Hashable b, Eq b) => m a -> (a -> m b) -> m b 15 16 instance HashableMonad HashSet where 17 return = singleton 18 m >>= f = HashSet.foldr (HashSet.union . f) empty m 19 20 -- | Possible destinations of a move originaing from (c, r). 21 possibleMoves :: (Num a, Ord a, Hashable a) => (a, a) -> HashSet (a, a) 22 possibleMoves (c, r) = 23 if (c < 0 || c > 7) || (r < 0 || r > 7) 24 then error "possibleMoves: invalid origin" 25 else let inBounds (x, y) = (x >= 0 && y >= 0) && (x <= 7 && y <= 7) 26 in fromList $ filter inBounds [ (c - 1, r - 2), (c - 1, r + 2) 27 , (c - 2, r - 1), (c - 2, r + 1) 28 , (c + 1, r - 2), (c + 1, r + 2) 29 , (c + 2, r - 1), (c + 2, r + 1) ] 30 {-# INLINE possibleMoves #-} 31 32 -- | The reachability of the destination from the origin in n steps. 33 canReachInN :: (Num a, Ord a, Hashable a) => (a, a) -> (a, a) -> a -> Bool 34 canReachInN (c0, r0) (c1, r1) = go (possibleMoves (c0, r0)) 35 where go !h !n | n <= 0 = False 36 | otherwise = HashSet.member (c1, r1) h 37 || go (h >>= possibleMoves) (n - 1) 38 {-# INLINE canReachInN #-} 39 40 -- | The number of moves required to reach the destination from the origin. 41 movesToReach :: (Num a, Ord a, Hashable a) => (a, a) -> (a, a) -> Integer 42 movesToReach a@(c0, r0) b@(c1, r1) 43 | (c1 < 0 || c1 > 7) || (r1 < 0 || r1 > 7) = 44 error "countMovesToReach: invalid destination" 45 | a == b = 0 46 | otherwise = go (possibleMoves (c0, r0)) 1 47 where go !h !n | HashSet.member b h = n 48 | otherwise = go (h >>= possibleMoves) (n + 1) 49 {-# INLINE movesToReach #-} 50 51 -- | Print the number of moves required to move from (0, 0) to (7, 7). 52 printNumMovesToOppositeCorner :: IO () 53 printNumMovesToOppositeCorner = print $ movesToReach (0 :: Int, 0 :: Int) 54 (7 :: Int, 7 :: Int) 55 56 main :: IO () 57 main = do putStrLn "Number of moves from (0, 0) to (7, 7)" 58 printNumMovesToOppositeCorner 59