This repository has been archived by the owner on Jun 13, 2023. It is now read-only.
forked from simonmar/parconc-examples
-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathSudoku.hs
150 lines (128 loc) · 5.16 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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
--
-- Sudoku solver using constraint propagation. The algorithm is by
-- Peter Norvig http://norvig.com/sudoku.html; the Haskell
-- implementation is by Manu and Daniel Fischer, and can be found on
-- the Haskell Wiki http://www.haskell.org/haskellwiki/Sudoku
--
-- The Haskell wiki license applies to this code:
--
-- Permission is hereby granted, free of charge, to any person obtaining
-- this work (the "Work"), to deal in the Work without restriction,
-- including without limitation the rights to use, copy, modify, merge,
-- publish, distribute, sublicense, and/or sell copies of the Work, and
-- to permit persons to whom the Work is furnished to do so.
--
-- THE WORK IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-- WITH THE WORK OR THE USE OR OTHER DEALINGS IN THE WORK.
module Sudoku (solve, printGrid) 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 '-'