1. John Chandler
  2. pseudohkoo


pseudohkoo / src / pseudohkoo / core.clj

(ns pseudohkoo.core)
(require 'clojure.set)

; NOTE: some of these functions will disappear - I've been writing them to
;       help me understand Clojure as well as aspects of the problem space

(defn extract-slice [vec index]
  ; extracts a three item slice from vec which corresponds to the slice of 3x3 box index is within
  ; this needs a better description!
  (let [start-index (* 3 (quot index 3))]
    (subvec vec start-index (+ start-index 3))))

(defn get-box [grid row-index column-index]
  ; return set of values of 3x3 box containing given location
  (set (flatten (map
		 (fn [row] (extract-slice row column-index))
		 (extract-slice grid row-index)))))

(defn get-column [grid column-index]
  ; returns the specified column from the grid
  (map (fn [row] (nth row column-index)) grid))

(defn get-row [grid row-index]
  ; returns the specified row from the grid, or [] if row out-of-bounds
  (nth grid row-index []))

(defn update-row [row column-index new-value]
  ; returns a copy of row with the indicated column set to new-value
  (let [head (take column-index row)
	tail (nthnext row (+ column-index 1))]
    (concat head [new-value] tail)))

(defn get-cell [grid row-index column-index]
  ; returns the value of the grid cell at the given location, or nil if out-of-bounds
  (nth (get-row grid row-index) column-index nil))

(defn format-value [value]
  ; returns either an integer or a vector, depending on whether
  ; value holds single or multiple values respectively
  (if (= 1 (count value))
    (first value)
    (vec value)))

(defn current-values [row]
  ; returns the set of known/definite current values for the row
  (set (filter integer? row)))

(defn required-values [row]
  ; returns the set of required values for the row
  ; i.e. the set of numbers needed for a row to be complete
  (set (range 1 (+ (count row) 1))))

(defn missing-values [row]
  ; returns the set of numbers that are still missing from the row
  (clojure.set/difference (required-values row) (current-values row)))

(defn refine-row [row]
  ; return a row with non-definite values refined, where possible
  (map (fn [cell]
	 (if (nil? cell)
	   (format-value (missing-values row))
	   (if (vector? cell)
	     (format-value (clojure.set/intersection (missing-values row) (set cell)))

(defn refine-grid [grid]
  ; return a "refined" grid - replacing nil cells with definite or possible values as appropriate 
  (map refine-row grid))

(defn row-complete? [row]
  ; returns true if row has been completed or false if not
  (= (required-values row) (set row)))

(defn grid-complete? [grid]
  ; returns true if grid is finished or false if not
  (reduce (fn [x y] (and x y)) (map row-complete? grid)))