module Test where import Control.DeepSeq import System.CPUTime import Text.Printf import System.Exit import qualified Exercise03 as Sub selectRow :: [[Int]] -> Int -> [Int] selectRow xss i = xss !! i selectColumn :: [[Int]] -> Int -> [Int] selectColumn xss i = [xs !! i | xs <- xss] intRoot :: Int -> Int intRoot = floor . sqrt . fromIntegral selectSquare :: [[Int]] -> Int -> [Int] selectSquare xss i = concat [selectColumns i xs | xs <- selectRows i] where squareSize = intRoot $ length xss selectRows i = take squareSize $ drop ((i `div` squareSize) * squareSize) xss selectColumns i xs = take squareSize $ drop ((i `mod` squareSize) * squareSize) xs isValidSubsection :: [Int] -> Bool isValidSubsection [] = True isValidSubsection (x:xs) | x == 0 || (x `notElem` xs) = isValidSubsection xs | otherwise = False isValidSudoku :: [[Int]] -> Bool isValidSudoku xss = and $ [x >= 0 && x <= length xss | x <- concat xss] ++ [ isValidSubsection (selectSquare xss i) && isValidSubsection (selectRow xss i) && isValidSubsection (selectColumn xss i) | i <- [0..(length xss-1)] ] chunksOf :: Int -> [e] -> [[e]] chunksOf i [] = [] chunksOf i ls = take i ls : chunksOf i (drop i ls) readSudokus :: FilePath -> IO [[[Int]]] readSudokus fp = do lin <- filter (\l -> head l /= '#') . lines <$> readFile fp return $ chunksOf 9 . map (read . pure . dot) <$> lin where dot '.' = '0' dot c =c containsSudoku :: [[Int]] -> [[Int]] -> Bool containsSudoku sol base = aux (concat sol) (concat base) where aux [] [] = True aux (s:ss) (b:bs) = (b == s || b == 0) && aux ss bs aux _ _ = False hasEmpty :: [[Int]] -> Bool hasEmpty s = 0 `elem` concat s doesSolveSudoku :: [[Int]] -> [[Int]] -> Bool doesSolveSudoku sol base = Sol.isValidSudoku sol && not (hasEmpty sol) && containsSudoku sol base subSolveSudoku :: [[Int]] -> Bool subSolveSudoku sudoku = doesSolveSudoku (force (Sub.solveSudoku sudoku)) sudoku getCPUTimeMs = (`div` (10 ^ 9)) <$> getCPUTime getCPUTimeUsed start = do curr <- getCPUTimeMs return $ curr - start solveSudokusWithin :: Integer -> Integer -> [[[Int]]] -> IO Int solveSudokusWithin _ _ [] = return 0 solveSudokusWithin start timeout (s : ss) = if subSolveSudoku s then do timeUsed <- getCPUTimeUsed start if timeUsed > timeout then return 0 else (+ 1) <$> solveSudokusWithin start timeout ss else exitWith (ExitFailure 1) main :: IO () main = do sudokus <- readSudokus "test-data/puzzles4_forum_hardest_1905" :: IO [[[Int]]] start <- getCPUTimeMs solvedSudokus <- solveSudokusWithin start (3 * 60000) sudokus print solvedSudokus