这是我在haskell中为Sudoku提供的代码。然而,我被困在一个应该找到特定细胞的候选者的功能上。例如:
candidates allBlankSudoku (8,8)
[1,2,3,4,5,6,7,8,9]
由于整个地图都是空白的,因此可以插入所有数字..
这是我的代码:
module Sudoku where
import Data.Char
import Data.List
import Data.Maybe
import Test.QuickCheck
data Sudoku = Sudoku[[Maybe Int]]
deriving (Show, Eq)
--data Sudoku = Sudoku { rows :: [[Maybe Int]] }
example :: Sudoku
example = Sudoku [[Just 3, Just 6, Nothing,Nothing,Just 7, Just 1, Just 2, Nothing,Nothing]
, [Nothing,Just 5, Nothing,Nothing,Nothing,Nothing,Just 1, Just 8, Nothing]
, [Nothing,Nothing,Just 9, Just 2, Nothing,Just 4, Just 7, Nothing,Nothing]
, [Nothing,Nothing,Nothing,Nothing,Just 1, Just 3, Nothing,Just 2, Just 8]
, [Just 4, Nothing,Nothing,Just 5, Nothing,Just 2, Nothing,Nothing,Just 9]
, [Just 2, Just 7, Nothing,Just 4, Just 6, Nothing,Nothing,Nothing,Nothing]
, [Nothing,Nothing,Just 5, Just 3, Nothing,Just 8, Just 9, Nothing,Nothing]
, [Nothing,Just 8, Just 3, Nothing,Nothing,Nothing,Nothing,Just 6, Nothing]
, [Nothing,Nothing,Just 7, Just 6, Just 9, Nothing,Nothing,Just 4, Just 3]
]
rows :: Sudoku -> [[Maybe Int]]
rows (Sudoku rs) = rs
-- A
-- Crates a blank sudoku "map"
allBlankSudoku :: Sudoku
allBlankSudoku = Sudoku (replicate 9 (replicate 9 Nothing))
-- Checks if the criterias meet the standard sudoku
isSudoku :: Sudoku -> Bool
isSudoku x = (length (rows x) == 9) && ( and [length y == 9 | y <- rows x]) && ( and [x > 0 && x < 10 | Just x <- concat (rows x)])
-- Checks if the sudoku puzzle is solved. Which is when there is no more cells to fill in
isSolved :: Sudoku -> Bool
isSolved x = Nothing `notElem` concat (rows x)
-- Prints a sudoko
printSudoku :: Sudoku -> IO()
printSudoku x = putStr (unlines ([ map convertInt y | y <- rows x]))
-- converting from int to char
convertInt :: Maybe Int -> Char
convertInt Nothing = '.'
convertInt (Just x) = chr (x+48)
-- simply reads sudoku from file
readSudoku :: FilePath -> IO Sudoku
readSudoku x =
do y <- readFile x
return (Sudoku [map convertChar c | c <- lines y])
-- Convertion from char to int.
convertChar :: Char -> Maybe Int
convertChar '.' = Nothing
convertChar x = Just (digitToInt x)
-- this function crates a cell
cell :: Gen (Maybe Int)
cell = frequency
[(1, do n <- choose (1,9)
return (Just n)),
(9, return Nothing)]
-- C2. Make Sudokus an instance of the class Arbitrary.
instance Arbitrary Sudoku where
arbitrary =
do rows <- sequence [ sequence [ cell | j <- [1..9] ] | i <- [1..9] ]
return (Sudoku rows)
-- check sudoku
prop_Sudoku :: Sudoku -> Bool
prop_Sudoku x = isSudoku x
type Block = [ Maybe Int]
-- CHeck if block containts same digit twice
isOkayBlock :: Block -> Bool
isOkayBlock x = x' == nub x'
where x' = filter isJust x
-- create list of all blocks
blocks :: Sudoku -> [Block]
blocks x = rows x ++ transpose (rows x) ++ blocks1 x
-- 3x3 made by blocks
blocks1 :: Sudoku -> [Block]
blocks1 x = [concat [take 3 (drop a b) | b <- take 3 (drop d (rows x))] | a <- [0,3,6] , d <- [0,3,6]]
-- checks the whole sudoku
isOkay :: Sudoku -> Bool
isOkay x = and (map isOkayBlock (blocks x))
prop_isOkay x = and [ length a == 9 | a <- blocks x ] && length (blocks x) == 27
type Pos = (Int,Int)
--way to find the blanks in sudoku.
blank :: Sudoku -> Pos -- Tells where a blank cell is located
blank x = head $ concat $ map blank' (zip [0..8] (rows x))
blank' :: (Int ,[Maybe Int]) -> [Pos] -- A help function for blank
blank' (x,y) = [(x,z) | (z,Nothing) <- zip [0..8] y]
--Replace/update
(!!=) :: [a] -> (Int ,a) -> [a]
(x:xs) !!= (0,a) = (a:xs)
(x:xs) !!= (y,a) = (x:(xs!!=(y-1,a)))
-- Update cell with new value
update :: Sudoku -> Pos -> Maybe Int -> Sudoku
update x (p,i) y = Sudoku ((rows x) !!= (p,z))
where z = (rows x) !! p !!= (i,y)
-- Check update function
prop_update (x,y) sud n = prop_XY (x',y') (update sud (x',y') n) == n
where x' = x `mod` 9
y' = y `mod` 9
-- helepr to find specific value
prop_XY (x,y) sud = ((!!) (rows (sud)) x) !! y
--candidates :: Sudoku -> Pos -> [Int]
-- any tips on how to solve this? we got stuck here...
solve :: Sudoku -> Maybe Sudoku
solve x
| not(isOkay x) = Nothing
| isSolved x = Just x
| otherwise = solve' [solve $ update x (blank x) (Just c) | c <- [1..9]]
-- take from our list solutions
solve' :: [Maybe a] -> Maybe a
solve' [] = Nothing
solve' (Nothing:xs) = solve' xs
solve' (Just x:xs) = Just x
-- read and solves the sudoku.
readAndSolve :: FilePath -> IO ()
readAndSolve x =
do y <- readFile x
printSudoku $ fromJust $ solve $ Sudoku [map convertChar i | i <- lines y]
-- check if x 2 solution to x1
isSolutionOf :: Sudoku -> Sudoku -> Bool
isSolutionOf x1 x2 = isOkay x1 && isSolved x1 && isSolutionOf2 (zip (concat(rows x1)) (concat(rows x2)))
-- helper func
isSolutionOf2 :: (Eq a) => [(Maybe a, Maybe a)] -> Bool
isSolutionOf2 [] = True
isSolutionOf2 ((x,y):xs) = x == y || y == Nothing && isSolutionOf2 xs
-- Solved or not
prop_SolveSound :: Sudoku -> Property
prop_SolveSound x = isOkay x ==> (fromJust (solve x)) `isSolutionOf` x
这就是函数的样子:
candidates :: Sudoku -> Pos -> [Int]