module Puzzle where import List type Board a = [[a]] -- 盤面 type Position = (Int,Int) -- マス目位置 putCell :: Board a -> (Position, a) -> Board a putCell b ((i,j),x) = ls0 ++ (cs0 ++ (x:cs1)):ls1 where (ls0,l:ls1) = splitAt j b (cs0,_:cs1) = splitAt i l class Eq a => Puzzle a where puzzleElm :: [a] -- マス目に入れるものリスト vacant :: a -- マス目に何も入っていないことを表すもの candidate :: Board a -> Position -> [a] -- 指定したマス目の位置に入れるものの候補(リスト) solve :: Puzzle a => Board a -> [Board a] solve b = case vacantPositions b of [] -> [b] ps -> case nextVacant b ps of (p,xs) -> concatMap solve $ map (putCell b) $ [ (p,x) | x <- xs ] vacantPositions :: Puzzle a => Board a -> [Position] vacantPositions b = map fst $ filter (\ (p,x) -> vacant == x) $ concatMap (\ (j,xs) -> zipWith (\ i x -> ((i,j),x)) [0..] xs ) $ zip [0..] b nextVacant :: Puzzle a => Board a -> [Position] -> (Position,[a]) nextVacant b ps = minimumBy cmp [ (p, candidate b p) | p <- ps ] where (_,xs) `cmp` (_,ys) = length xs `compare` length ys showBoard :: (Puzzle a, Show a) => Board a -> String showBoard = unlines . map (unwords . map show) readBoard :: (Puzzle a, Read a) => String -> Board a readBoard = map (map read) . map words . lines