(ns fun.sudoku
(:refer-clojure :exclude [==])
(:require [clojure.core.logic :as logic]
[clojure.core.logic.fd :as fd]))
;; 定义数独数据
(def b1 '[- - 1 - - - 2 - -
- 6 - - - 5 - 1 3
- - - - 1 - 7 - 6
6 - - 5 - 9 - - 8
- - 8 3 - 4 - 9 -
9 - - - - - - 2 -
- 1 - - - - - - 2
- 5 6 7 - - - - -
2 - 7 - - 1 5 - -])
;; 定义一些函数
(defn print-board [board] ;; 打印数独题目
(let [row-sep (apply str (repeat 37 "-"))
bx (map #(partition 3 %) (partition 9 board))]
(println row-sep)
(dotimes [row (count bx)]
(print "| ")
(doseq [subrow (nth bx row)]
(doseq [cell (butlast subrow)]
(print (str cell " ")))
(print (str (last subrow) " | ")))
(println)
(when (zero? (mod (inc row) 3))
(println row-sep)))))
;; 按行分组
(defn rowify [board]
(->> board
(partition 9)
(map vec)
vec))
;; 按列分组
(defn colify [rows]
(apply map vector rows))
;; 按3x3分组
(defn subgrid [rows]
(partition 9
(for [row (range 0 9 3)
col (range 0 9 3)
x (range row (+ row 3))
y (range col (+ col 3))]
(get-in rows [x y]))))
;; 接下来定义逻辑
;; 按数独数据初始化“知识”
(defn init [[lv & lvs] [cell & cells]]
(if lv
(logic/fresh []
(if (= '- cell)
logic/succeed
(logic/== lv cell))
(init lvs cells))
logic/succeed))
;; 初始化逻辑变量
(def logic-board #(repeatedly 81 logic/lvar))
;; ok,开始推理
(defn solve-logically [board]
(let [legal-nums (fd/interval 1 9)
lvars (logic-board)
rows (rowify lvars)
cols (colify rows)
grids (subgrid rows)]
(logic/run 1 [q]
(init lvars board)
(logic/everyg #(fd/in % legal-nums) lvars) ;; 条件1: 只能填1-9
(logic/everyg fd/distinct rows) ;; 条件2: 行不能重复
(logic/everyg fd/distinct cols) ;; 条件3: 列不能重复
(logic/everyg fd/distinct grids) ;; 条件4: 3x3方格内不能重复
(logic/== q lvars)))) ;; 结果
(comment
(-> b1
print-board)
(-> b1
solve-logically
first
print-board))
网友评论