(module sudoku mzscheme
(require (planet "inference.ss" ("williams" "inference.plt")))
(require (lib "list.ss" "srfi" "1"))
(provide (all-defined))
(define-ruleset sudoku-rules)
(define-rule (initialize sudoku-rules)
(board ?board)
(no (cell . ?))
==>
(printf "initialize:~n")
(print-board ?board)
(do ((row 0 (+ row 1)))
((= row 9) (void))
(assert `(digit ,row))
(do ((column 0 (+ column 1)))
((= column 9) (void))
(let ((value (vector-ref (vector-ref ?board row) column))
(box (+ (* (quotient row 3) 3)
(quotient column 3))))
(if (eqv? value '_)
(assert `(cell ,row ,column ,box (1 2 3 4 5 6 7 8 9)))
(assert `(cell ,row ,column ,box ,value)))))))
(define-rule (rule-1 sudoku-rules)
(all (cell ?row ?column ?box (?value (number? ?value))))
(board ?board)
==>
(stop-inference ?board))
(define-rule (rule-2 sudoku-rules)
(cell ?row ?column ?box (?value (eq? ?value '())))
==>
(fail))
(define-rule (rule-3 sudoku-rules)
(?cell <- (cell ?row ?column ?box
(?value (and (list? ?value) (= (length ?value) 1)))))
(board ?board)
==>
(vector-set! (vector-ref ?board ?row) ?column (car ?value))
(replace ?cell `(cell ,?row ,?column ,?box ,(car ?value))))
(define-rule (rule-4a sudoku-rules)
(cell ?row ?column ?box (?value (number? ?value)))
(cell ?row (?column-1 (not (= ?column-1 ?column))) ?box-1
(?value-1 (and (number? ?value-1) (= ?value-1 ?value))))
==>
(fail))
(define-rule (rule-4b sudoku-rules)
(cell ?row ?column ?box (?value (number? ?value)))
(cell (?row-1 (not (= ?row-1 ?row))) ?column ?box-1
(?value-1 (and (number? ?value-1) (= ?value-1 ?value))))
==>
(fail))
(define-rule (rule-4c sudoku-rules)
(cell ?row ?column ?box (?value (number? ?value)))
(cell ?row-1 ?column-1 (?box (or (not (= ?row-1 ?row))
(not (= ?column-1 ?column))))
(?value-1 (and (number? ?value-1) (= ?value-1 ?value))))
==>
(fail))
(define-rule (rule-5a sudoku-rules)
(cell ?row ?column ?box
(?value (number? ?value)))
(?cell-1 <- (cell ?row
(?column-1 (not (= ?column-1 ?column)))
?box-1
(?value-1 (and (list? ?value-1)
(memv ?value ?value-1)))))
==>
(replace ?cell-1 `(cell ,?row ,?column-1 ,?box-1
,(delete ?value ?value-1))))
(define-rule (rule-5b sudoku-rules)
(cell ?row ?column ?box
(?value (number? ?value)))
(?cell-1 <- (cell (?row-1 (not (= ?row-1 ?row)))
?column
?box-1
(?value-1 (and (list? ?value-1)
(memv ?value ?value-1)))))
==>
(replace ?cell-1 `(cell ,?row-1 ,?column ,?box-1
,(delete ?value ?value-1))))
(define-rule (rule-5c sudoku-rules)
(cell ?row ?column ?box
(?value (number? ?value)))
(?cell-1 <- (cell ?row-1
?column-1
(?box (or (not (= ?row-1 ?row))
(not (= ?column-1 ?column))))
(?value-1 (and (list? ?value-1)
(memv ?value ?value-1)))))
==>
(replace ?cell-1 `(cell ,?row-1 ,?column-1 ,?box
,(delete ?value ?value-1))))
(define-rule (rule-6a sudoku-rules)
(digit ?digit)
(?cell <- (cell ?row ?column ?box (?value (and (list? ?value)
(memv ?digit ?value)))))
(no (cell ?row (?column-1 (not (= ?column-1 ?column))) ?
(?value-1 (or (and (number? ?value-1)
(= ?value-1 ?digit))
(and (list? ?value-1)
(memv ?digit ?value-1))))))
==>
(replace ?cell `(cell ,?row ,?column ,?box ,(list ?digit))))
(define-rule (rule-6b sudoku-rules)
(digit ?digit)
(?cell <- (cell ?row ?column ?box (?value (and (list? ?value)
(memv ?digit ?value)))))
(no (cell (?row-1 (not (= ?row-1 ?row))) ?column ?
(?value-1 (or (and (number? ?value-1)
(= ?value-1 ?digit))
(and (list? ?value-1)
(memv ?digit ?value-1))))))
==>
(replace ?cell `(cell ,?row ,?column ,?box ,(list ?digit))))
(define-rule (rule-6c sudoku-rules)
(digit ?digit)
(?cell <- (cell ?row ?column ?box (?value (and (list? ?value)
(memv ?digit ?value)))))
(no (cell ?row-1 ?column-1 (?box (or (not (= ?row-1 ?row))
(not (= ?column-1 ?column))))
(?value-1 (or (and (number? ?value-1)
(= ?value-1 ?digit))
(and (list? ?value-1)
(memv ?digit ?value-1))))))
==>
(replace ?cell `(cell ,?row ,?column ,?box ,(list ?digit))))
(define-rule (search sudoku-rules #:priority -100)
(board ?board)
(cell ?row ?column ?box (?value (and (list? ?value)
(> (length ?value) 1))))
(no (cell ? ? ? (?value-1 (and (list? ?value-1)
(< (length ?value-1)
(length ?value))))))
==>
(printf "search: row = ~a, column = ~a, box = ~a, values = ~a~n"
?row ?column ?box ?value)
(for-each
(lambda (value)
(let ((new-board (copy-board ?board)))
(vector-set! (vector-ref new-board ?row) ?column value)
(let ((result
(with-new-child-inference-environment
(activate sudoku-rules)
(assert `(board ,new-board))
(let ((result (start-inference)))
(printf "Rules fired = ~a~n" (current-inference-rules-fired))
result))))
(when (vector? result)
(stop-inference result)))))
?value)
(fail))
(define (copy-board board)
(let ((new-board (make-vector 9)))
(do ((row 0 (+ row 1)))
((= row 9) new-board)
(let ((board-row (vector-ref board row))
(new-row (make-vector 9)))
(do ((column 0 (+ column 1)))
((= column 9) (void))
(vector-set! new-row column
(vector-ref board-row column)))
(vector-set! new-board row new-row)))))
(define (print-board board)
(do ((row 0 (+ row 1)))
((= row 9) (void))
(do ((column 0 (+ column 1)))
((= column 9) (void))
(let ((value (vector-ref (vector-ref board row) column)))
(printf "~a " value)))
(printf "~n")))
(define (sudoku-solver board)
(printf "Initial Board~n")
(print-board board)
(with-new-inference-environment
(activate sudoku-rules)
(assert `(board ,board))
(let ((result (start-inference)))
(cond ((eq? result #:fail)
(printf "Problem cannot be solved!~n"))
((not result)
(printf "No solution found!~n")
(let* ((board (cdr (assq '?board (cdar (query '(board ?board)))))))
(print-board board)))
(else
(printf "Solution found!~n")
(print-board result))))
(printf "Rules fired = ~a~n" (current-inference-rules-fired))
))
(sudoku-solver '#(#(_ 1 9 _ _ _ _ _ _)
#(_ _ 8 _ _ 3 _ 5 _)
#(_ 7 _ 6 _ _ _ 8 _)
#(_ _ 1 _ _ 6 8 _ 9)
#(8 _ _ _ 4 _ _ _ 7)
#(9 4 _ _ _ _ _ 1 _)
#(_ _ _ _ _ 2 _ _ _)
#(_ _ _ _ 8 _ 5 6 1)
#(_ _ 3 7 _ _ _ 9 _)))
)