[Haskell-cafe] The Knight's Tour: solutions please
Don Stewart
dons at galois.com
Sun Nov 30 20:53:48 EST 2008
ajb:
> G'day all.
>
> Quoting Don Stewart <dons at galois.com>:
>
> >So, team, anyone want to implement a Knight's Tour solver in a list
> >monad/list comprehension one liner? These little puzzles are made for
> >fast languages with backtracking monads....
>
> I conjecture that any one-liner won't be efficient.
>
> Anyway, here's my ~30 min attempt. The showBoard and main are both very
> quick and dirty, and I'm sure someone can do much better.
>
> I particularly like the fact that changing "Maybe" to "[]" will make
> knightsTour return all tours starting at the upper left-hand corner,
> rather than just one. Warm fuzzy things rule.
>
> Cheers,
> Andrew Bromage
>
> module Main where
>
> import qualified Data.Set as S
> import Data.List
> import Data.Function
> import Control.Arrow
> import Control.Monad
> import System
>
> knightsTour :: Int -> Maybe [(Int,Int)]
> knightsTour size
> = tour [(0,0)] (S.fromAscList [ (x,y) | x <- [0..size-1], y <-
> [0..size-1],
> x /= 0 || y /= 0 ])
> where
> jumps = [(2,1),(1,2),(2,-1),(-1,2),(-2,1),(1,-2),(-2,-1),(-1,-2)]
> tour moves@(pos:_) blank
> | S.null blank = return (reverse moves)
> | otherwise = msum [ tour (npos:moves) (npos `S.delete` blank) |
> npos <- nextPositions pos ]
> where
> nextPositions = map snd . sortBy (compare `on` fst) .
> map (length . neighbours &&& id) .
> neighbours
> neighbours (x,y) = [ npos | (x',y') <- jumps,
> let { npos = (x+x',y+y') }, npos `S.member` blank ]
>
> showBoard :: Int -> [(Int,Int)] -> ShowS
> showBoard size
> = inter bdr .
> map (inter ('|':) . map (shows . fst)) .
> groupBy ((==) `on` fst.snd) .
> sortBy (compare `on` snd) .
> zip [1..]
> where
> bdr = ('\n':) . inter ('+':) (replicate size (replicate width '-'
> ++))
> . ('\n':)
> width = length . show $ size*size
> pad s = \r -> replicate (width - length (s "")) ' ' ++ s r
> inter sep xs = sep . foldr (.) id [ pad x . sep | x <- xs ]
>
> main :: IO ()
> main = do
> a <- getArgs
> size <- case a of
> [] -> return 8
> (s:_) -> return (read s)
> putStrLn $ case knightsTour size of
> Nothing -> "No solution found."
> Just b -> showBoard size b ""
dolio implemented a cute one based on continuations, that's also about
10x faster than the python version,
http://hpaste.org/12546#a2
-- Don
More information about the Haskell-Cafe
mailing list