2024-03-21 13:56:57 +01:00
|
|
|
module Main where
|
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
import Data.Char (digitToInt)
|
|
|
|
import Data.Maybe (fromJust, fromMaybe, isJust, mapMaybe)
|
2024-03-21 17:27:24 +01:00
|
|
|
import qualified Data.Vector as V
|
2024-03-21 13:56:57 +01:00
|
|
|
import Types
|
2024-06-15 13:21:06 +02:00
|
|
|
( Grid (Grid),
|
|
|
|
allCells,
|
|
|
|
colView,
|
|
|
|
count,
|
|
|
|
getCell,
|
|
|
|
initGrid,
|
|
|
|
mapGrid,
|
|
|
|
printGrid,
|
|
|
|
putCell,
|
|
|
|
rowView,
|
|
|
|
squareView,
|
|
|
|
)
|
2024-03-21 13:56:57 +01:00
|
|
|
|
|
|
|
main :: IO ()
|
|
|
|
main = do
|
2024-06-15 13:21:06 +02:00
|
|
|
putStr $ printSudoku dummyGrid
|
|
|
|
print $ isValidGrid dummyGrid
|
|
|
|
putStr . printGrid . fromJust $ solve dummyGrid
|
2024-03-21 17:27:24 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
test :: String
|
|
|
|
test = "070000043040009610800634900094052000358460020000800530080070091902100005007040802"
|
2024-03-21 17:27:24 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
dummyGrid :: Grid (Maybe Int)
|
|
|
|
dummyGrid = initGrid 3 (map toMaybeInt test)
|
|
|
|
where
|
|
|
|
toMaybeInt :: Char -> Maybe Int
|
|
|
|
toMaybeInt c = if int == 0 then Nothing else Just int
|
|
|
|
where
|
|
|
|
int = digitToInt c
|
2024-03-21 17:27:24 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
printSudoku :: Grid (Maybe Int) -> String
|
|
|
|
printSudoku gr = printGrid mappedGrid
|
|
|
|
where
|
|
|
|
mappedGrid :: Grid Int
|
|
|
|
mappedGrid = mapGrid (fromMaybe 0) gr
|
2024-03-21 17:27:24 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
isValidGrid :: Grid (Maybe Int) -> Bool
|
|
|
|
isValidGrid gr@(Grid _ d) =
|
|
|
|
all isValidRow [0 .. d * d - 1]
|
|
|
|
&& all isValidCol [0 .. d * d - 1]
|
|
|
|
&& all isValidSquare [0 .. d * d - 1]
|
|
|
|
where
|
|
|
|
validView :: V.Vector (Maybe Int) -> Bool
|
|
|
|
validView v = V.foldl (\drag (e, c) -> case e of Nothing -> drag; Just _ -> (c < 2) && drag) True counts
|
|
|
|
where
|
|
|
|
counts = V.map (\e -> (e, count v e)) v
|
|
|
|
isValidRow :: Int -> Bool
|
|
|
|
isValidRow y = validView $ rowView gr y
|
|
|
|
isValidCol :: Int -> Bool
|
|
|
|
isValidCol x = validView $ colView gr x
|
|
|
|
isValidSquare :: Int -> Bool
|
|
|
|
isValidSquare n = validView $ squareView gr n
|
2024-03-21 13:56:57 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
isFull :: Grid (Maybe Int) -> Bool
|
|
|
|
isFull gr = allCells gr isJust
|
|
|
|
|
|
|
|
isSolved :: Grid (Maybe Int) -> Bool
|
2024-06-16 15:35:14 +02:00
|
|
|
isSolved gr = isValidGrid gr && isFull gr
|
2024-06-15 13:21:06 +02:00
|
|
|
|
|
|
|
extractSolution :: Grid (Maybe Int) -> Grid Int
|
|
|
|
extractSolution gr =
|
|
|
|
if not (isSolved gr)
|
|
|
|
then error "trying to extract solution from unsolved grid"
|
|
|
|
else mapGrid (fromMaybe 0) gr
|
|
|
|
|
|
|
|
solveHelper :: Grid (Maybe Int) -> (Int, Int) -> Maybe (Grid Int)
|
|
|
|
solveHelper gr@(Grid _ d) (x, y)
|
|
|
|
| not (isValidGrid gr) = Nothing
|
|
|
|
| isSolved gr = Just (extractSolution gr)
|
|
|
|
| otherwise = case cell of
|
|
|
|
Nothing -> if not (null solvedGrids) then head solvedGrids else Nothing
|
|
|
|
Just _ -> solveHelper gr nextCoord
|
|
|
|
where
|
|
|
|
placeAndSolve :: Int -> Maybe (Grid Int)
|
|
|
|
placeAndSolve n = solveHelper newGrid nextCoord
|
|
|
|
where
|
|
|
|
newGrid = putCell gr (x, y) (Just n)
|
|
|
|
solvedGrids = map Just (mapMaybe placeAndSolve [1 .. d * d])
|
|
|
|
cell = getCell gr (x, y)
|
|
|
|
nextCoord = if x < d * d - 1 then (x + 1, y) else (0, y + 1)
|
2024-03-21 17:27:24 +01:00
|
|
|
|
2024-06-15 13:21:06 +02:00
|
|
|
solve :: Grid (Maybe Int) -> Maybe (Grid Int)
|
|
|
|
solve gr = solveHelper gr (0, 0)
|