Dan Doel dan.doel at gmail.com
Sun Nov 30 21:12:18 EST 2008

```Here's a clean-up of my code (it even fits within the line-length limit of my
mail client :)). Note that it's pretty much exactly the Python algorithm. When
the Python program finds a solution, it prints the board and exits. Since
that's evil IO type stuff, we noble functional folk instead set up an exit
continuation using callCC, and call it when we find a solution. :)

I haven't bothered testing it against the Python version, but the backtracking
solution I wrote with the Logic monad (and Data.Map) took around 50% more time
than this.

-- Dan

---- snip ----

module Main where

import Data.Array.ST
import Data.List
import Data.Ord
import Data.Ix

import System.Environment

type Square  = (Int, Int)
type Board s = STUArray s (Int,Int) Int
type ChessM r s = ContT r (ST s)
type ChessK r s = String -> ChessM r s ()

successors :: Int -> Board s -> Square -> ChessM r s [Square]
successors n b = sortWith (fmap length . succs) <=< succs
where
sortWith f l = map fst `fmap` sortBy (comparing snd)
`fmap` mapM (\x -> (,) x `fmap` f x) l
succs (i,j) = filterM (empty b)
[ (i', j') | (dx,dy) <- [(1,2),(2,1)]
, i' <- [i+dx,i-dx] , j' <- [j+dy, j-dy]
, inRange ((1,1),(n,n)) (i',j') ]

empty :: Board s -> Square -> ChessM r s Bool
empty b s = fmap (<1) . lift \$ readArray b s

mark :: Square -> Int -> Board s -> ChessM r s ()
mark s k b = lift \$ writeArray b s k

tour :: Int -> Int -> ChessK r s -> Square -> Board s -> ChessM r s ()
tour n k exit s b | k > n*n   = showBoard n b >>= exit
| otherwise = successors n b s >>=
mapM_ (\x -> do mark x k b
tour n (k+1) exit x b
-- failed, rollback
mark x 0 b)

showBoard :: Int -> Board s -> ChessM r s String
showBoard n b = fmap unlines . forM [1..n] \$ \i ->
fmap unwords . forM [1..n] \$ \j ->
where
k = floor . log . fromIntegral \$ n*n
pad i = let s = show i in replicate (k-length s) ' ' ++ s

main = do (n:_) <- map read `fmap` getArgs
s <- stToIO . flip runContT return \$
(do b <- lift \$ newArray ((1,1),(n,n)) 0
mark (1,1) 1 b
callCC \$ \k -> tour n 2 k (1,1) b >> fail "No solution!")
putStrLn s

```