Implements grid pruning and solve

custom-accumulator
Abhinav Sarkar 2018-06-17 16:24:46 +05:30
parent 30ebffa1b5
commit 97da7c92f9
1 changed files with 80 additions and 5 deletions

View File

@ -1,6 +1,9 @@
module Main where module Main where
import qualified Control.Applicative
import qualified Control.Monad
import qualified Data.Char import qualified Data.Char
import qualified Data.Function
import qualified Data.List.Split import qualified Data.List.Split
import qualified Data.List import qualified Data.List
@ -8,10 +11,6 @@ data Cell = Fixed Int | Possible [Int] deriving (Show, Eq)
type Row = [Cell] type Row = [Cell]
type Grid = [Row] type Grid = [Row]
possibleVals :: Cell -> [Int]
possibleVals (Fixed x) = [x]
possibleVals (Possible xs) = xs
readGrid :: String -> Maybe Grid readGrid :: String -> Maybe Grid
readGrid s readGrid s
| length s == 81 = | length s == 81 =
@ -38,5 +37,81 @@ showGridWithPossibilities = unlines . map (unwords . map showCell)
. Data.List.foldl' (\acc x -> acc ++ if x `elem` xs then show x else " ") "[" . Data.List.foldl' (\acc x -> acc ++ if x `elem` xs then show x else " ") "["
$ [1..9] $ [1..9]
pruneCells :: [Cell] -> Maybe [Cell]
pruneCells cells = sequence . map pruneCell $ cells
where
fixeds = [x | Fixed x <- cells]
pruneCell (Possible xs) = case xs Data.List.\\ fixeds of
[] -> Nothing
[y] -> Just $ Fixed y
ys -> Just $ Possible ys
pruneCell x = Just x
main = undefined blocksToRows :: Grid -> Grid
blocksToRows =
concatMap (\rows -> let (r1:r2:r3:_) = map (Data.List.Split.chunksOf 3) rows
in zipWith3 (\a b c -> a ++ b ++ c) r1 r2 r3)
. Data.List.Split.chunksOf 3
pruneGrid :: Grid -> Maybe Grid
pruneGrid = fixM pruneGrid'
pruneGrid' grid =
sequence (map pruneCells grid)
>>= fmap Data.List.transpose . sequence . map pruneCells . Data.List.transpose
>>= fmap blocksToRows . sequence . map pruneCells . blocksToRows
fixM f x = f x >>= \x' -> if x' == x then return x else fixM f x'
isInvalidGrid :: Grid -> Bool
isInvalidGrid grid =
any isInvalidRow grid
|| any isInvalidRow (Data.List.transpose grid)
|| any isInvalidRow (blocksToRows grid)
where
isInvalidRow = not . isValidRow
isValidRow row =
let fixeds = [x | Fixed x <- row]
emptyPossibles = [x | Possible x <- row, null x]
in length fixeds == length (Data.List.nub fixeds) && null emptyPossibles
isFinishedGrid :: Grid -> Bool
isFinishedGrid grid = null [ () | Possible _ <- concat grid ]
solve :: Grid -> Maybe Grid
solve grid
| isInvalidGrid grid = Nothing
| isFinishedGrid grid = Just grid
| otherwise =
let (grid1, grid2) = splitGrid
in case pruneGrid grid1 of
Nothing -> pruneGrid grid2 >>= solve
Just grid' -> solve grid' Control.Applicative.<|> (pruneGrid grid2 >>= solve)
where
possibleVals (Fixed x) = [x]
possibleVals (Possible xs) = xs
smallestPossible =
head
. Data.List.sortBy (compare `Data.Function.on` (length . possibleVals . snd))
$ [(i, c) | (i, c@(Possible _)) <- zip [0..] $ concat grid]
splitPossible (i, Possible (x:[y])) = (i, Fixed x, Fixed y)
splitPossible (i, Possible (x:xs)) = (i, Fixed x, Possible xs)
splitPossible _ = error "Impossible case"
splitGrid =
let (i, first@(Fixed _), rest) = splitPossible smallestPossible
in (replace2D i first grid, replace2D i rest grid)
replace2D i v = let (x, y) = (i `quot` 9, i `mod` 9) in replace x (replace y (const v))
replace p f xs = [if i == p then f x else x | (x, i) <- zip xs [0..]]
main = do
grids <- lines <$> getContents
Control.Monad.forM_ grids $ \grid ->
case readGrid grid of
Nothing -> putStrLn "Invalid grid"
Just grid -> case pruneGrid grid >>= solve of
Nothing -> putStrLn "No solution found"
Just grid' -> putStrLn $ showGrid grid'