import List

sudokuBase, sudokuSize :: Int
sudokuBase = 3
sudokuSize = sudokuBase ^ 2

type Sudoku = Int

sudokuElm :: [Sudoku]
sudokuElm = [1..sudokuSize]

vacant :: Sudoku
vacant = 0

type SudokuBoard = [[Sudoku]]

sample :: SudokuBoard
sample = [[8,0,0,0,3,4,0,5,0]
         ,[0,0,2,0,0,0,0,0,1]
         ,[0,1,0,9,0,0,0,0,0]
         ,[0,0,8,0,0,9,0,0,6]
         ,[5,0,0,0,1,0,0,0,8]
         ,[6,0,0,4,0,0,7,0,0]
         ,[0,0,0,0,0,1,0,7,0]
         ,[2,0,0,0,0,0,1,0,0]
         ,[0,9,0,5,6,0,0,0,2]]

type Position = (Int,Int) -- (列,行)

sudoku :: SudokuBoard -> [SudokuBoard]
sudoku b
 = case vacantPositions b of
    [] -> [b]                            -- 空白のマス目が無い → 終了
    ps -> case nextVacant b ps of        -- 空白のマス目が有る
            (p,xs) ->                    -- 空白のマス目の位置 p と候補リスト xs
                      concatMap sudoku   -- 新しい盤面をそれぞれ解いて集める
                    $ map (putCell b)    -- 各ペアで更新した盤面を作る
                    $ [(p,x) | x <- xs]  -- 位置と候補のペアのリスト

vacantPositions :: SudokuBoard -> [Position]
nextVacant      :: SudokuBoard -> [Position] -> (Position,[Sudoku])
putCell         :: SudokuBoard -> (Position,Sudoku) -> SudokuBoard

putCell b ((i,j), x) = ls0++(xs0++x:xs1):ls1
                       where 
                         (ls0,l:ls1) = splitAt j b
                         (xs0,_:xs1) = splitAt i l

nextVacant b ps = minimumBy cmp [(p,candidate b p) | p <- ps]
  where
    (_,xs) `cmp` (_,ys) = length xs `compare` length ys

candidate :: SudokuBoard -> Position -> [Sudoku]
candidate b p = sudokuElm \\ nub (concat $ map (\ c -> c b p) [col,row,box])

col, row, box :: SudokuBoard -> Position -> [Sudoku]
col b (i,_) = transpose b !! i
row b (_,j) = b !! j
box b (i,j) = concat
            $ map (take sudokuBase) $ map (drop $ (i `div` sudokuBase) * sudokuBase)
            $ take sudokuBase $ drop ((j `div` sudokuBase) * sudokuBase) b

vacantPositions b = map fst
                     $ filter (\ (p,x) -> vacant == x)
                     $ concatMap (\ (j,xs) -> zipWith (\ i x -> ((i,j),x)) [0..] xs )
                     $ zip [0..] b

showSudokuBoard :: SudokuBoard -> String
showSudokuBoard b = unlines $ map (unwords . map show) b

readSudokuBoard :: String -> SudokuBoard
readSudokuBoard s = map (map read) $ map words $ lines s

instance Show SudokuBoard where
  show = showSudokuBoard

instance Read SudokuBoard where
  readsPrec _ str = [(readSudokuBoard str, "")]

