Clojure逻辑式编程求解数独

2018-04-26  本文已影响0人  mingdongz
(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))
上一篇 下一篇

猜你喜欢

热点阅读