-
Notifications
You must be signed in to change notification settings - Fork 17
/
Copy pathsudoku.hs
133 lines (111 loc) · 4.14 KB
/
sudoku.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module Main where
import Data.List hiding (lookup)
import Data.Array
import Control.Monad
import Data.Maybe
-- Types
type Digit = Char
type Square = (Char,Char)
type Unit = [Square]
-- We represent our grid as an array
type Grid = Array Square [Digit]
-- Setting Up the Problem
rows = "ABCDEFGHI"
cols = "123456789"
digits = "123456789"
box = (('A','1'),('I','9'))
cross :: String -> String -> [Square]
cross rows cols = [ (r,c) | r <- rows, c <- cols ]
squares :: [Square]
squares = cross rows cols -- [('A','1'),('A','2'),('A','3'),...]
peers :: Array Square [Square]
peers = array box [(s, set (units!s)) | s <- squares ]
where
set = nub . concat
unitlist :: [Unit]
unitlist = [ cross rows [c] | c <- cols ] ++
[ cross [r] cols | r <- rows ] ++
[ cross rs cs | rs <- ["ABC","DEF","GHI"],
cs <- ["123","456","789"]]
-- this could still be done more efficiently, but what the heck...
units :: Array Square [Unit]
units = array box [(s, [filter (/= s) u | u <- unitlist, s `elem` u ]) |
s <- squares]
allPossibilities :: Grid
allPossibilities = array box [ (s,digits) | s <- squares ]
-- Parsing a grid into an Array
parsegrid :: String -> Maybe Grid
parsegrid g = do regularGrid g
foldM assign allPossibilities (zip squares g)
where regularGrid :: String -> Maybe String
regularGrid g = if all (`elem` "0.-123456789") g
then Just g
else Nothing
-- Propagating Constraints
assign :: Grid -> (Square, Digit) -> Maybe Grid
assign g (s,d) = if d `elem` digits
-- check that we are assigning a digit and not a '.'
then do
let ds = g ! s
toDump = delete d ds
foldM eliminate g (zip (repeat s) toDump)
else return g
eliminate :: Grid -> (Square, Digit) -> Maybe Grid
eliminate g (s,d) =
let cell = g ! s in
if d `notElem` cell then return g -- already eliminated
-- else d is deleted from s' values
else do let newCell = delete d cell
newV = g // [(s,newCell)]
newV2 <- case newCell of
-- contradiction : Nothing terminates the computation
[] -> Nothing
-- if there is only one value left in s, remove it from peers
[d'] -> do let peersOfS = peers ! s
foldM eliminate newV (zip peersOfS (repeat d'))
-- else : return the new grid
_ -> return newV
-- Now check the places where d appears in the peers of s
foldM (locate d) newV2 (units ! s)
locate :: Digit -> Grid -> Unit -> Maybe Grid
locate d g u = case filter ((d `elem`) . (g !)) u of
[] -> Nothing
[s] -> assign g (s,d)
_ -> return g
-- Search
search :: Grid -> Maybe Grid
search g =
case [(l,(s,xs)) | (s,xs) <- assocs g, let l = length xs, l /= 1] of
[] -> return g
ls -> do let (_,(s,ds)) = minimum ls
msum [assign g (s,d) >>= search | d <- ds]
solve :: String -> Maybe Grid
solve str = do
grd <- parsegrid str
search grd
-- Display solved grid
printGrid :: Grid -> IO ()
printGrid = putStrLn . gridToString
gridToString :: Grid -> String
gridToString g =
let l0 = elems g
-- [("1537"),("4"),...]
l1 = (map (\s -> " " ++ s ++ " ")) l0
-- ["1 "," 2 ",...]
l2 = (map concat . sublist 3) l1
-- ["1 2 3 "," 4 5 6 ", ...]
l3 = (sublist 3) l2
-- [["1 2 3 "," 4 5 6 "," 7 8 9 "],...]
l4 = (map (concat . intersperse "|")) l3
-- ["1 2 3 | 4 5 6 | 7 8 9 ",...]
l5 = (concat . intersperse [line] . sublist 3) l4
in unlines l5
where sublist n [] = []
sublist n xs = ys : sublist n zs
where (ys,zs) = splitAt n xs
line = hyphens ++ "+" ++ hyphens ++ "+" ++ hyphens
hyphens = replicate 9 '-'
main :: IO ()
main = do
grids <- fmap lines $ readFile "top95.txt"
mapM_ printGrid $ mapMaybe solve grids