hasdoku/src/Sudoku.hs

207 lines
6.9 KiB
Haskell
Raw Normal View History

2018-06-17 00:12:23 +05:30
module Main where
2018-06-21 11:32:32 +05:30
import Control.Applicative ((<|>))
import Data.Function ((&))
import Data.Vector ((!))
2018-06-17 16:24:46 +05:30
import qualified Control.Monad
import qualified Control.Monad.ST
2018-06-17 00:12:23 +05:30
import qualified Data.Char
2018-06-17 16:24:46 +05:30
import qualified Data.Function
2018-06-17 00:12:23 +05:30
import qualified Data.List
import qualified Data.Map.Strict as Map
2018-06-30 12:57:01 +05:30
import qualified Data.Word
import qualified Data.Bits
2018-07-17 23:10:17 +05:30
import qualified Data.Vector
import qualified Data.Vector.Unboxed
import qualified Data.Vector.Unboxed.Mutable
import qualified Data.STRef
fixM :: (Eq t, Monad m) => (t -> m t) -> t -> m t
fixM f x = f x >>= \x' -> if x' == x then return x else fixM f x'
2018-06-17 00:12:23 +05:30
2018-06-30 12:57:01 +05:30
setBits :: Data.Word.Word16 -> [Data.Word.Word16] -> Data.Word.Word16
setBits = Data.List.foldl' (Data.Bits..|.)
data Cell = Fixed Data.Word.Word16
| Possible Data.Word.Word16
deriving (Show, Eq)
2018-07-17 23:10:17 +05:30
type Grid = Data.Vector.Vector Cell
type CellIxs = [Int]
2018-06-17 00:12:23 +05:30
isPossible :: Cell -> Bool
isPossible (Possible _) = True
isPossible _ = False
2018-06-17 00:12:23 +05:30
readGrid :: String -> Maybe Grid
2018-06-17 11:54:22 +05:30
readGrid s
2018-07-17 23:10:17 +05:30
| length s == 81 = Data.Vector.fromList <$> traverse readCell s
2018-06-17 11:54:22 +05:30
| otherwise = Nothing
2018-06-17 00:12:23 +05:30
where
2018-06-30 12:57:01 +05:30
allBitsSet = 1022
readCell '.' = Just $ Possible allBitsSet
2018-06-17 00:12:23 +05:30
readCell c
2018-06-30 12:57:01 +05:30
| Data.Char.isDigit c && c > '0' = Just . Fixed . Data.Bits.bit . Data.Char.digitToInt $ c
2018-06-17 00:12:23 +05:30
| otherwise = Nothing
2018-07-17 23:10:17 +05:30
fromXY :: (Int, Int) -> Int
fromXY (x, y) = x * 9 + y
allRowIxs, allColIxs, allSubGridIxs :: [CellIxs]
allRowIxs = [getRow i | i <- [0..8]]
where getRow n = [ fromXY (n, i) | i <- [0..8] ]
allColIxs = [getCol i | i <- [0..8]]
where getCol n = [ fromXY (i, n) | i <- [0..8] ]
allSubGridIxs = [getSubGrid i | i <- [0..8]]
where getSubGrid n = let (r, c) = (n `quot` 3, n `mod` 3)
in [ fromXY (3 * r + i, 3 * c + j) | i <- [0..2], j <- [0..2] ]
replaceCell :: Int -> Cell -> Grid -> Grid
replaceCell i c g = g Data.Vector.// [(i, c)]
2018-06-17 00:12:23 +05:30
showGrid :: Grid -> String
2018-07-17 23:10:17 +05:30
showGrid grid = unlines . map (unwords . map (showCell . (grid !))) $ allRowIxs
2018-06-17 00:12:23 +05:30
where
2018-06-30 12:57:01 +05:30
showCell (Fixed x) = show . Data.Bits.countTrailingZeros $ x
showCell _ = "."
2018-06-17 00:12:23 +05:30
showGridWithPossibilities :: Grid -> String
2018-07-17 23:10:17 +05:30
showGridWithPossibilities grid = unlines . map (unwords . map (showCell . (grid !))) $ allRowIxs
2018-06-17 00:12:23 +05:30
where
2018-06-30 12:57:01 +05:30
showCell (Fixed x) = (show . Data.Bits.countTrailingZeros $ x) ++ " "
2018-06-17 00:12:23 +05:30
showCell (Possible xs) =
2018-06-30 12:57:01 +05:30
"[" ++ map (\i -> if Data.Bits.testBit xs i then Data.Char.intToDigit i else ' ') [1..9] ++ "]"
2018-06-17 00:12:23 +05:30
cellIndicesList :: [Cell] -> [Data.Word.Word16]
cellIndicesList cells =
Data.Vector.Unboxed.toList $ Control.Monad.ST.runST $ do
vec <- Data.Vector.Unboxed.Mutable.replicate 9 Data.Bits.zeroBits
ref <- Data.STRef.newSTRef (1 :: Int)
Control.Monad.forM_ cells $ \cell -> do
i <- Data.STRef.readSTRef ref
case cell of
Fixed _ -> return ()
Possible xs -> Control.Monad.forM_ [0..8] $ \d ->
Control.Monad.when (Data.Bits.testBit xs (d+1)) $
Data.Vector.Unboxed.Mutable.unsafeModify vec (`Data.Bits.setBit` i) d
Data.STRef.writeSTRef ref (i+1)
Data.Vector.Unboxed.unsafeFreeze vec
2018-06-30 12:57:01 +05:30
exclusivePossibilities :: [Cell] -> [Data.Word.Word16]
exclusivePossibilities row =
row
& cellIndicesList
& zip [1..9]
& filter (\(_, xs) -> let p = Data.Bits.popCount xs in p > 0 && p < 4)
& Data.List.foldl' (\acc (x, is) -> Map.insertWith prepend is [x] acc) Map.empty
& Map.filterWithKey (\is xs -> Data.Bits.popCount is == length xs)
& Map.elems
2018-06-30 12:57:01 +05:30
& map (Data.List.foldl' Data.Bits.setBit Data.Bits.zeroBits)
where
prepend ~[y] ys = y:ys
2018-06-30 12:57:01 +05:30
makeCell :: Data.Word.Word16 -> Maybe Cell
makeCell ys
| ys == Data.Bits.zeroBits = Nothing
| Data.Bits.popCount ys == 1 = Just $ Fixed ys
| otherwise = Just $ Possible ys
pruneCells :: Grid -> CellIxs -> Maybe Grid
pruneCells grid cellIxs = Control.Monad.foldM pruneCell grid cellIxs
2018-06-17 16:24:46 +05:30
where
cells = map (grid !) cellIxs
exclusives = exclusivePossibilities cells
allExclusives = setBits Data.Bits.zeroBits exclusives
fixeds = setBits Data.Bits.zeroBits [x | Fixed x <- cells]
pruneCell g i =
pruneCellByFixed g (i, g ! i) >>= \g' -> pruneCellByExclusives g' (i, g' ! i)
pruneCellByFixed g (_, Fixed _) = Just g
pruneCellByFixed g (i, Possible xs)
2018-07-17 23:10:17 +05:30
| xs' == xs = Just g
| otherwise = flip (replaceCell i) g <$> makeCell xs'
where
xs' = xs Data.Bits..&. Data.Bits.complement fixeds
pruneCellByExclusives g (_, Fixed _) = Just g
pruneCellByExclusives g (i, Possible xs)
| null exclusives = Just g
2018-07-17 23:10:17 +05:30
| intersection == xs = Just g
| intersection `elem` exclusives =
flip (replaceCell i) g <$> makeCell intersection
| otherwise = Just g
where
2018-06-30 12:57:01 +05:30
intersection = xs Data.Bits..&. allExclusives
pruneGrid' :: Grid -> Maybe Grid
2018-06-17 16:24:46 +05:30
pruneGrid' grid =
2018-07-17 23:10:17 +05:30
Control.Monad.foldM pruneCells grid allRowIxs
>>= flip (Control.Monad.foldM pruneCells) allColIxs
>>= flip (Control.Monad.foldM pruneCells) allSubGridIxs
2018-06-17 16:24:46 +05:30
pruneGrid :: Grid -> Maybe Grid
pruneGrid = fixM pruneGrid'
2018-06-17 16:24:46 +05:30
2018-06-26 11:48:14 +05:30
isGridFilled :: Grid -> Bool
2018-07-17 23:10:17 +05:30
isGridFilled = not . Data.Vector.any isPossible
2018-06-21 11:32:32 +05:30
2018-06-26 11:48:14 +05:30
isGridInvalid :: Grid -> Bool
isGridInvalid grid =
2018-07-17 23:10:17 +05:30
any isInvalidRow (map (map (grid !)) allRowIxs)
|| any isInvalidRow (map (map (grid !)) allColIxs)
|| any isInvalidRow (map (map (grid !)) allSubGridIxs)
2018-06-17 16:24:46 +05:30
where
2018-06-21 11:32:32 +05:30
isInvalidRow row =
2018-06-30 12:57:01 +05:30
let fixeds = [x | Fixed x <- row]
emptyPossibles = [() | Possible x <- row, x == Data.Bits.zeroBits]
2018-06-21 11:32:32 +05:30
in hasDups fixeds || not (null emptyPossibles)
2018-06-17 16:24:46 +05:30
2018-06-21 11:32:32 +05:30
hasDups l = hasDups' l []
hasDups' [] _ = False
hasDups' (y:ys) xs
| y `elem` xs = True
| otherwise = hasDups' ys (y:xs)
2018-06-17 16:24:46 +05:30
2018-06-26 11:48:14 +05:30
nextGrids :: Grid -> (Grid, Grid)
nextGrids grid =
let (i, first@(Fixed _), rest) =
fixCell
2018-07-17 23:10:17 +05:30
. Data.Vector.minimumBy (compare `Data.Function.on` (possibilityCount . snd))
. Data.Vector.imapMaybe (\j cell -> if isPossible cell then Just (j, cell) else Nothing)
2018-06-26 11:48:14 +05:30
$ grid
2018-07-17 23:10:17 +05:30
in (replaceCell i first grid, replaceCell i rest grid)
2018-06-21 11:32:32 +05:30
where
2018-06-30 12:57:01 +05:30
possibilityCount (Possible xs) = Data.Bits.popCount xs
2018-06-26 11:48:14 +05:30
possibilityCount (Fixed _) = 1
2018-06-21 11:32:32 +05:30
2018-06-30 12:57:01 +05:30
fixCell ~(i, Possible xs) =
let x = Data.Bits.countTrailingZeros xs
in case makeCell (Data.Bits.clearBit xs x) of
Nothing -> error "Impossible case"
Just cell -> (i, Fixed (Data.Bits.bit x), cell)
2018-06-21 11:32:32 +05:30
2018-06-26 11:48:14 +05:30
solve :: Grid -> Maybe Grid
solve grid = pruneGrid grid >>= solve'
where
2018-06-27 15:27:34 +05:30
solve' g
| isGridInvalid g = Nothing
| isGridFilled g = Just g
| otherwise =
let (grid1, grid2) = nextGrids g
2018-06-26 11:48:14 +05:30
in solve grid1 <|> solve grid2
main :: IO ()
2018-06-17 16:24:46 +05:30
main = do
2018-06-26 11:48:14 +05:30
inputs <- lines <$> getContents
Control.Monad.forM_ inputs $ \input ->
case readGrid input of
Nothing -> putStrLn "Invalid input"
2018-06-21 11:32:32 +05:30
Just grid -> case solve grid of
2018-06-26 11:48:14 +05:30
Nothing -> putStrLn "No solution found"
2018-06-17 16:24:46 +05:30
Just grid' -> putStrLn $ showGrid grid'