praxis

Various programming exercises.
Log | Files | Refs

commit 6509527adb21e3e2af4ab33afb78a2db4fa3b983
parent 6fc10e62d21cf2e1cdc860049428b413dea8b225
Author: Jared Tobin <jared@jtobin.ca>
Date:   Sun,  7 Apr 2013 16:37:15 +1200

Add old knight-move problem.

Diffstat:
A20130308_knight/knight.hs | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 59 insertions(+), 0 deletions(-)

diff --git a/20130308_knight/knight.hs b/20130308_knight/knight.hs @@ -0,0 +1,59 @@ +{-# OPTIONS_GHC -Wall #-} +{-# LANGUAGE BangPatterns #-} + +module Main where + +import Prelude hiding (Monad, return, (>>=)) +import qualified Data.HashSet as HashSet +import Data.HashSet (HashSet, fromList, empty, singleton) +import Data.Hashable (Hashable) + +-- | Standard monad class with Hashable/Eq constraints on parameters. +class HashableMonad m where + return :: Hashable a => a -> m a + (>>=) :: (Hashable a, Hashable b, Eq b) => m a -> (a -> m b) -> m b + +instance HashableMonad HashSet where + return = singleton + m >>= f = HashSet.foldr (HashSet.union . f) empty m + +-- | Possible destinations of a move originaing from (c, r). +possibleMoves :: (Num a, Ord a, Hashable a) => (a, a) -> HashSet (a, a) +possibleMoves (c, r) = + if (c < 0 || c > 7) || (r < 0 || r > 7) + then error "possibleMoves: invalid origin" + else let inBounds (x, y) = (x >= 0 && y >= 0) && (x <= 7 && y <= 7) + in fromList $ filter inBounds [ (c - 1, r - 2), (c - 1, r + 2) + , (c - 2, r - 1), (c - 2, r + 1) + , (c + 1, r - 2), (c + 1, r + 2) + , (c + 2, r - 1), (c + 2, r + 1) ] +{-# INLINE possibleMoves #-} + +-- | The reachability of the destination from the origin in n steps. +canReachInN :: (Num a, Ord a, Hashable a) => (a, a) -> (a, a) -> a -> Bool +canReachInN (c0, r0) (c1, r1) = go (possibleMoves (c0, r0)) + where go !h !n | n <= 0 = False + | otherwise = HashSet.member (c1, r1) h + || go (h >>= possibleMoves) (n - 1) +{-# INLINE canReachInN #-} + +-- | The number of moves required to reach the destination from the origin. +movesToReach :: (Num a, Ord a, Hashable a) => (a, a) -> (a, a) -> Integer +movesToReach a@(c0, r0) b@(c1, r1) + | (c1 < 0 || c1 > 7) || (r1 < 0 || r1 > 7) = + error "countMovesToReach: invalid destination" + | a == b = 0 + | otherwise = go (possibleMoves (c0, r0)) 1 + where go !h !n | HashSet.member b h = n + | otherwise = go (h >>= possibleMoves) (n + 1) +{-# INLINE movesToReach #-} + +-- | Print the number of moves required to move from (0, 0) to (7, 7). +printNumMovesToOppositeCorner :: IO () +printNumMovesToOppositeCorner = print $ movesToReach (0 :: Int, 0 :: Int) + (7 :: Int, 7 :: Int) + +main :: IO () +main = do putStrLn "Number of moves from (0, 0) to (7, 7)" + printNumMovesToOppositeCorner +