From 5a74507c79caac7f05e2499d7c51b0d70260ab87 Mon Sep 17 00:00:00 2001 From: Ray Miller Date: Sat, 24 Jul 2010 12:39:47 +0100 Subject: [PATCH] Initial check-in --- .gitignore | 5 ++ README | 15 ++++ project.clj | 5 ++ src/ray1729/clojure/sudoku/core.clj | 91 +++++++++++++++++++++++ test/ray1729/clojure/sudoku/core_test.clj | 6 ++ 5 files changed, 122 insertions(+) create mode 100644 .gitignore create mode 100644 README create mode 100644 project.clj create mode 100644 src/ray1729/clojure/sudoku/core.clj create mode 100644 test/ray1729/clojure/sudoku/core_test.clj diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a02f899 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +pom.xml +*jar +lib +classes* +*~ diff --git a/README b/README new file mode 100644 index 0000000..6db7e16 --- /dev/null +++ b/README @@ -0,0 +1,15 @@ +# ray1729.clojure.sudoku + +FIXME: write description + +## Usage + +FIXME: write + +## Installation + +FIXME: write + +## License + +FIXME: write diff --git a/project.clj b/project.clj new file mode 100644 index 0000000..c7fb8b4 --- /dev/null +++ b/project.clj @@ -0,0 +1,5 @@ +(defproject ray1729.clojure.sudoku "1.0.0-SNAPSHOT" + :description "FIXME: write" + :dependencies [[org.clojure/clojure "1.2.0-master-SNAPSHOT"] + [org.clojure/clojure-contrib "1.2.0-SNAPSHOT"]] + :dev-dependencies [[swank-clojure "1.2.1"]]) diff --git a/src/ray1729/clojure/sudoku/core.clj b/src/ray1729/clojure/sudoku/core.clj new file mode 100644 index 0000000..25f9b5f --- /dev/null +++ b/src/ray1729/clojure/sudoku/core.clj @@ -0,0 +1,91 @@ +(ns ray1729.clojure.sudoku.core + (:use [clojure.contrib.string :only (join)]) + (:require [clojure.contrib.error-kit :as ekit])) + +(ekit/deferror *inconsistent-grid* [] [] + {:msg "Inconsistent grid" + :unhandled (ekit/throw-msg Exception)}) + +(def rows "ABCDEFGHI") +(def cols "123456789") +(def grid-keys (for [r rows c cols] (str r c))) +(def candidates (apply hash-set "123456789")) + +(def empty-grid (apply sorted-map (interleave grid-keys (repeat candidates)))) + +(defn print-grid [grid] + (letfn [(cell->str [cell] (join "" (map #(or (cell %) \.) candidates))) + (row->str [row] (join " | " (map #(join " " %) (partition 3 (map cell->str row)))))] + (let [rows (map row->str (partition 9 (vals grid))) + separator "------------------------------+-------------------------------+------------------------------"] + (doseq [r (apply concat (interpose [separator] (partition 3 rows)))] (println r))))) + +(defn row-keys [[r c]] (map #(str r %) cols)) + +(defn col-keys [[r c]] (map #(str % c) rows)) + +(defn box-keys [[r c]] + (let [row-map (into {} (for [ps (partition 3 rows) e ps] [e ps])) + col-map (into {} (for [ps (partition 3 cols) e ps] [e ps]))] + (for [row (row-map r) col (col-map c)] (str row col)))) + +(defn units [s] (vector (row-keys s) (col-keys s) (box-keys s))) + +(defn peers [s] (disj (into #{} (apply concat (units s))) s)) + +(declare assign) +(declare eliminate) + +(defn parse-grid [s] + (let [values (filter #(not (= \0 (val %))) (zipmap grid-keys (map first (re-seq #"\d" s))))] + (reduce #(assign %1 (key %2) (val %2)) empty-grid values))) + +(defn assign [grid cell value] + (if ((grid cell) value) + (reduce #(eliminate %1 %2 value) (assoc grid cell #{value}) (peers cell)) + (ekit/raise *inconsistent-grid*))) + +(defn eliminate [grid cell value] + (if (not ((grid cell) value)) + grid + (let [new-candidates (disj (grid cell) value) + num-candidates (count new-candidates)] + (cond + (= num-candidates 0) (ekit/raise *inconsistent-grid*) + (= num-candidates 1) (assign grid cell (first new-candidates)) + :else (assoc grid cell new-candidates))))) + +(defn solved? [grid] + (every? #(= 1 (count %)) (vals grid))) + +(defn solve [grid] + (if (solved? grid) + grid + (let [cell (first (filter #(> (count (grid %)) 1) (keys grid))) + candidates (grid cell) + candidate (first candidates)] + (ekit/with-handler + (do + (println (str "Trying " cell "=" candidate)) + (recur (assign grid cell candidate))) + (ekit/handle *inconsistent-grid* [] + (if (next candidates) + (do (println (str "Trying next candidate for " cell)) + (recur (eliminate grid cell candidate))) + (ekit/do-not-handle))))))) + +(comment + + (def g1 (parse-grid + "003020600 + 900305001 + 001806400 + 008102900 + 700000008 + 006708200 + 002609500 + 800203009 + 005010300")) + + (def g2 (parse-grid "400000805030000000000700000020000060000080400000010000000603070500200000104000000")) + ) \ No newline at end of file diff --git a/test/ray1729/clojure/sudoku/core_test.clj b/test/ray1729/clojure/sudoku/core_test.clj new file mode 100644 index 0000000..73f5020 --- /dev/null +++ b/test/ray1729/clojure/sudoku/core_test.clj @@ -0,0 +1,6 @@ +(ns ray1729.clojure.sudoku.core-test + (:use [ray1729.clojure.sudoku.core] :reload-all) + (:use [clojure.test])) + +(deftest replace-me ;; FIXME: write + (is false))