toysolver-0.2.0: samples/programs/sudoku/sudoku.hs
{-# LANGUAGE ScopedTypeVariables #-}
{-# OPTIONS_GHC -Wall #-}
module Main where
import Control.Monad
import Data.Array.IArray
import Data.Char
import System.Environment
import System.Exit
import System.IO
import qualified ToySolver.SAT as SAT
main :: IO ()
main = do
args <- getArgs
case args of
[fname] -> do
s <- readFile fname
let b = readBoard $ filter (not . isSpace) $ removeComments s
ans <- solve b
case ans of
Nothing -> hPutStrLn stderr "NO SOLUTION"
Just b2 -> do
hPutStrLn stderr "SOLUTION FOUND"
printAnswer b2
_ -> do
hPutStrLn stderr "Usage: sudoku input.sdk"
exitFailure
removeComments :: String -> String
removeComments = unlines . filter p . lines
where
p ('#':_) = False
p _ = True
readBoard :: String -> Array (Int,Int) (Maybe Int)
readBoard s = array ((1,1),(9,9)) $
[ (idx, if isDigit c then Just (read [c]) else Nothing)
| (idx, c) <- zip [(i,j) | i <- [1..9], j <- [1..9]] s
]
solve :: Array (Int,Int) (Maybe Int) -> IO (Maybe (Array (Int,Int) Int))
solve board = do
solver <- SAT.newSolver
SAT.setLogger solver (hPutStrLn stderr)
(vs :: Array (Int,Int,Int) SAT.Var) <-
liftM (array ((1,1,1), (9,9,9))) $
forM [(i,j,k) | i<-[1..9], j<-[1..9], k<-[1..9]] $ \idx -> do
v <- SAT.newVar solver
return (idx,v)
forM_ (assocs board) $ \((i,j), m) -> do
case m of
Nothing -> return ()
Just k -> SAT.addClause solver [vs ! (i,j,k)]
-- Every cell contains exactly one digit.
forM_ [1..9] $ \i -> do
forM_ [1..9] $ \j -> do
SAT.addExactly solver [vs ! (i,j,k) | k <- [1..9]] 1
forM_ [1..9] $ \k -> do
-- Every row contains exactly one occurence of the digit.
forM_ [1..9] $ \i -> do
SAT.addExactly solver [vs ! (i,j,k) | j <- [1..9]] 1
-- Every column contains exactly one occurence of the digit.
forM_ [1..9] $ \j -> do
SAT.addExactly solver [vs ! (i,j,k) | i <- [1..9]] 1
-- Every block contains exactly one occurence of the digit.
forM_ [0..2] $ \bi ->
forM_ [0..2] $ \bj ->
SAT.addExactly solver [vs ! (bi*3 + i', bj*3 + j', k) | i' <- [1..3], j' <- [1..3]] 1
ret <- SAT.solve solver
if ret then do
m <- SAT.getModel solver
return $ Just $ array ((1,1),(9,9)) [((i,j),k) | ((i,j,k),v) <- assocs vs, m ! v]
else
return Nothing
printAnswer :: Array (Int,Int) Int -> IO ()
printAnswer b =
forM_ [1..9] $ \i ->
putStrLn $ concat [show (b ! (i,j)) | j <- [1..9]]