(defn cross [A, B]
(for [a A b B] (str a b)))
(def rows "ABCDEFGHI")
(def cols "123456789")
(def digits "123456789")
(def subsquaresize 3)
(def rowgroups (partition subsquaresize rows))
(def colgroups (partition subsquaresize cols))
(def separators "0.-")
(def squares (cross rows cols))
(def unitlist (map set (concat
(for [c cols] (cross rows [c]))
(for [r rows] (cross [r] cols))
(for [rs rowgroups
cs colgroups] (cross rs cs)))))
(defn dict [x] (apply sorted-map (apply concat x)))
(defn set-union [x] (apply sorted-set (apply concat x)))
(defn all? [coll] (every? identity coll))
(def units (dict (for [s squares]
[s (for [u unitlist :when (u s)] u)] )))
(def peers (dict (for [s squares]
[s (disj (set-union (units s)) s)])))
(declare assign! eliminate! check!)
(defn strip-grid [grid] (filter (set (concat digits separators)) grid))
(defn make-grid [] (dict (for [s squares] [s,(atom digits)])))
(defn parse_grid [grid]
(let [grid (strip-grid grid)
values (make-grid)]
(if (all? (for [[square digit] (zipmap squares grid) :when ((set digits) digit)]
(assign! values square digit)))
values
false)))
(defn assign! [values square digit]
(if (all? (for [d @(values square) :when (not (= d digit))]
(eliminate! values square d)))
values
false))
(defn eliminate! [values s d]
(if (not ((set @(values s)) d)) values (do
(swap! (values s) #(. % replace (str d) "")) (if (= 0 (count @(values s))) false (if (= 1 (count @(values s))) (let [d2 (first @(values s))]
(if (not (all? (for [s2 (peers s)] (eliminate! values s2 d2))))
false
(check! values s d)))
(check! values s d))))))
(defn check! [values s d]
(loop [u (units s)] (let [dplaces (for [s (first u) :when ((set @(values s)) d)] s)] (if (= (count dplaces) 0) false
(if (= (count dplaces) 1) (if (not (assign! values (first dplaces) d)) false
(if (not (empty? (rest u))) (recur (rest u)) values))
(if (not (empty? (rest u))) (recur (rest u)) values))))))
(defn centre[s width]
(let [pad (- width (count s))
lpad (int (/ pad 2))
rpad (- pad lpad)]
(str (apply str (repeat lpad " ")) s (apply str (repeat rpad " ")))))
(defn join [char seq]
(apply str (interpose char seq)))
(defmacro forjoin [sep [var seq] body]
`(join ~sep (for [~var ~seq] ~body)))
(defn board [values]
(if (= values false)
"no solution"
(let [ width (+ 2 (apply max (for [s squares] (count @(values s)))))
line (str \newline
(join \+ (repeat subsquaresize
(join \- (repeat subsquaresize
(apply str (repeat width "-"))))))
\newline)]
(forjoin line [rg rowgroups]
(forjoin "\n" [r rg]
(forjoin "|" [cg colgroups]
(forjoin " " [c cg]
(centre @(values (str r c)) width))))))))
(defn print_board [values] (println (board values)))
(defn deepcopy [values] (dict (for [k (keys values)] [k (atom @(values k))])))
(defn search
([values] (search values ""))
([values, recurse]
(println "recursion: " recurse)
(if values
(if (all? (for [s squares] (= 1 (count @(values s))))) values (let [ pivot
(second (first (sort (for [s squares :when (>(count @(values s)) 1)]
[(count @(values s)),s]))))]
(let [results (for [d @(values pivot)] (do (search (assign! (deepcopy values) pivot d) (str recurse d))))] (some identity results))))
false)))
(def hardestsudokuinworld "
850002400
720000009
004000000
000107002
305000900
040000000
000080070
017000000
000036040
")
(defn solve [grid]
(do
(println "\nproblem:")
(println (join \newline (map #(apply str %) (partition 9 (filter (set (concat digits separators)) grid)))))
(println "\nsolution:")
(print_board (search (parse_grid grid)))))
(solve hardestsudokuinworld)
(use 'clojure.contrib.str-utils)
(use 'clojure.contrib.duck-streams)
(def easy-sudokus (re-split #"\s*Grid\s.*\s*" (slurp "sudoku.txt")))
(def hard-sudokus (read-lines "sudoku_hard.txt"))
(defn show-off []
(solve hardestsudokuinworld)
(doall (map solve easy-sudokus))
(doall (map solve hard-sudokus)))
No comments:
Post a Comment