Hi Clojure fans
My first attempt with Clojure is a Sudoku solver, and I'm fighting
with what are probably trivial problems (for a non newbie)

The main function sudoku is recursive:
1. Getting a sudoku board as an input
2. Choosing the next empty (zero) cell to test, loop on all valid
values, and call sudoku with the new board
3. When a solution (board with no zero values) is found: throw.

(defn sudoku [board]
  "solve a sudoku problem"
  (when (complete? board)
    (do
     (println "complete")
     (print-board board)
      (throw nil)))
  (let [cell (next-cell board)
       pos (first cell)
       valid-values (second cell)]
       (when cell
         (doseq [v valid-values]
                (sudoku (assoc-in board pos v)))
         )))

Although it does work, we can all agree its pretty ugly, so I would
appreciate your help on the following questions:
1. How to can I return a solution via the recursive stack with out
throwing an exception? I understand there is no "return-from"
facility.
2. Can this function be implemented as tail recursive (using loop?
recur?)

Naturally, any other inputs are welcome.

Thanks
Tzach

---------------
Full source:

(defn print-board [board]
  "Pretty print the sudoku board"
  (doseq [row board] (println row)))

(defn mod3range [x]
  (let [start (- x (rem x 3))]
       (range start (+ 3 start))))

(defn neighbors-pos [pos]
  "return a collection of neighbors positions to pos"
  (remove #(= pos %)
          (distinct (concat
                     (for [y (range 3) z (range 3)] [(get pos 0) y z]) ; row
neighbors
                     (for [x (range 9)] [x (get pos 1) (get pos 2)]) ; col 
neighbors
                     (for [x (mod3range (get pos 0)) z (range 3)] [x (get pos 1)
z]))) ; square neighbors;
          ))

(defn neighbors-values [board pos]
      "return a list of neighbor positions values"
      (map #(get-in board %) (neighbors-pos pos)))

(defn valid-values [board pos]
      "return a list of values which does not violate the neighbors
values. return nil if the position already have a value"
      (if (zero? (get-in board pos))
          (clojure.set/difference (set (range 1 10)) (neighbors-values board
pos))
          (seq nil)))

(defn map-board [board f]
      "execute function f on each of the position on board.  function
f get the position and the board as parameters"
       (for [x (range 9) y (range 3) z (range 3)]
            (let [pos [x y z]]
                 [pos (f board pos) ]
                 )))

(defn next-cell [board]
  "return the next potential cell to set, and the valid alternatives"
  (first (filter
          #(not (empty? (second %)))
          (sort-by #(count (second %)) (map-board board valid-values)))))

(defn complete? [board]
  (not (some #(second %)
             (map-board board (fn [board pos] (zero? (get-in board pos)))))))


(defn sudoku [board]
  "solve a sudoku problem"
  (when (complete? board)
    (do
     (println "complete")
     (print-board board)
      (throw nil)))
  (let [cell (next-cell board)
       pos (first cell)
       valid-values (second cell)]
       (when cell
         (doseq [v valid-values]
                (sudoku (assoc-in board pos v)))
         )))

;;; use the solver
(def *sudoku-problem*
 [[[0 0 0] [5 0 0] [0 9 1]]
  [[1 0 0] [8 6 0] [0 3 2]]
  [[0 0 6] [9 3 0] [0 0 0]]
  [[0 0 4] [6 0 0] [0 7 3]]
  [[0 5 0] [4 9 3] [0 1 0]]
  [[3 6 0] [0 0 8] [9 0 0]]
  [[0 0 0] [0 8 5] [3 0 0]]
  [[8 3 0] [0 1 6] [0 0 5]]
  [[6 7 0] [0 0 9] [0 0 0]]])

(sudoku *sudoku-problem*)


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to