在haskell中写Sudoku,查找单元格的可能候选人

时间:2016-12-13 16:46:24

标签: haskell sudoku

这是我在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]

0 个答案:

没有答案