diff --git a/csp/hacs.rkt b/csp/hacs.rkt index a5e0e552..1eb3d4b9 100644 --- a/csp/hacs.rkt +++ b/csp/hacs.rkt @@ -10,6 +10,12 @@ (struct $avar $var () #:transparent) (struct inconsistency-signal (csp) #:transparent) +(define current-select-variable (make-parameter #f)) +(define current-order-values (make-parameter #f)) +(define current-inference (make-parameter #f)) +(define current-solver (make-parameter #f)) + + (define/contract (check-name-in-csp! caller csp name) (symbol? $csp? $var-name? . -> . void?) (define names (map $var-name ($csp-vars csp))) @@ -29,7 +35,8 @@ ($var-vals ($csp-var csp name))) (define order-domain-values values) -(define (assign-val csp name val) +(define/contract (assign-val csp name val) + ($csp? $var-name? any/c . -> . $csp?) ($csp (for/list ([var ($csp-vars csp)]) (if (eq? name ($var-name var)) @@ -37,49 +44,52 @@ var)) ($csp-constraints csp))) -(define current-select-variable (make-parameter #f)) -(define current-order-values (make-parameter #f)) -(define current-inference (make-parameter #f)) - -(define (first-unassigned-variable csp) - (for/first ([var ($csp-vars csp)] +(define/contract (first-unassigned-variable csp) + ($csp? . -> . (or/c #false (and/c $var? (not/c $avar?)))) + (for/first ([var (in-list ($csp-vars csp))] #:unless ($avar? var)) var)) -(check-equal? (first-unassigned-variable ($csp (list ($var 'a (range 3)) ($var 'b (range 3))) null)) ($var 'a (range 3))) -(check-equal? (first-unassigned-variable ($csp (list ($avar 'a (range 3)) ($var 'b (range 3))) null)) ($var 'b (range 3))) +(check-equal? (first-unassigned-variable ($csp (list ($var 'a (range 3)) ($var 'b (range 3))) null)) + ($var 'a (range 3))) +(check-equal? (first-unassigned-variable ($csp (list ($avar 'a (range 3)) ($var 'b (range 3))) null)) + ($var 'b (range 3))) (check-false (first-unassigned-variable ($csp (list ($avar 'a (range 3)) ($avar 'b (range 3))) null))) (define first-domain-value values) (define (no-inference csp name) csp) -(define (forward-check csp aname) +(define/contract (relating constraints names) + ((listof $constraint?) (listof $var-name?) . -> . (listof $constraint?)) + (for*/list ([constraint (in-list constraints)] + [cnames (in-value ($constraint-names constraint))] + #:when (for/and ([name (in-list names)]) + (memq name cnames))) + constraint)) + +(define/contract (forward-check csp aname) + ($csp? $var-name? . -> . $csp?) (define aval (first ($csp-vals csp aname))) (define (filter-vals var) (match-define ($var name vals) var) (define new-vals - (match (for*/list ([constraint (in-list ($csp-constraints csp))] - [cnames (in-value ($constraint-names constraint))] - #:when (and (memq aname cnames) (memq name cnames))) - constraint) - [(list) vals] + (match (($csp-constraints csp) . relating . (list aname name)) + [(? empty?) vals] [constraints - (for*/list ([val (in-list vals)] - [constraint (in-list constraints)] - [cnames (in-value ($constraint-names constraint))] - #:when (cond - [(eq? (first cnames) name) - (($constraint-proc constraint) val aval)] - [(eq? (second cnames) name) - (($constraint-proc constraint) aval val)] - [else #true])) + (for/list ([val (in-list vals)] + #:when (for/and ([constraint (in-list constraints)]) + (let ([proc ($constraint-proc constraint)]) + (if (eq? name (first ($constraint-names constraint))) + (proc val aval) + (proc aval val))))) val)])) - (unless (pair? new-vals) (raise (inconsistency-signal csp))) + (unless (pair? new-vals) + (raise (inconsistency-signal csp))) new-vals) ($csp - (for/list ([var ($csp-vars csp)]) + (for/list ([var (in-list ($csp-vars csp))]) (if ($avar? var) var ($var ($var-name var) (filter-vals var)))) @@ -90,14 +100,14 @@ (list ($avar 'a '(1)) ($var 'b '(0 1)))) (check-equal? - ;; no inconsistency because b≠c not checked (because fc is relative to a) + ;; no inconsistency: b≠c not checked when fc is relative to a ($csp-vars (forward-check ($csp (list ($avar 'a '(1)) ($var 'b (range 2)) ($var 'c '(0))) (list ($constraint '(a b) (negate =)) ($constraint '(b c) (negate =)))) 'a)) (list ($avar 'a '(1)) ($var 'b '(0)) ($var 'c '(0)))) (check-equal? - ;; no inconsistency because a≠b not checked (because fc ignores a, which is already assigned) + ;; no inconsistency: a≠b not checked when fc ignores a, which is already assigned ($csp-vars (forward-check ($csp (list ($avar 'a '(1)) ($avar 'b '(1)) ($var 'c (range 2))) (list ($constraint '(a b) (negate =)) ($constraint '(b c) (negate =)))) 'b)) @@ -108,11 +118,21 @@ ($var 'b '(1))) (list ($constraint '(a b) (negate =)))) 'a)))) -(define/contract (backtrack-solution-generator csp - [select-unassigned-variable (or (current-select-variable) first-unassigned-variable)] - [order-domain-values (or (current-order-values) first-domain-value)] - [inference (or (current-inference) no-inference)]) - (($csp?) (procedure? procedure? procedure?) . ->* . generator?) + +(check-equal? ($csp-vars (forward-check ($csp (list ($avar 'a (range 3)) + ($var 'b (range 3))) + (list ($constraint '(a b) <) + ($constraint '(a b) <) + ($constraint '(a b) <))) 'a)) + (list ($avar 'a '(0 1 2)) ($var 'b '(1 2)))) + + +(define/contract (backtracking-solver + csp + #:select-variable [select-unassigned-variable (or (current-select-variable) first-unassigned-variable)] + #:order-values [order-domain-values (or (current-order-values) first-domain-value)] + #:inference [inference (or (current-inference) no-inference)]) + (($csp?) (#:select-variable procedure? #:order-values procedure? #:inference procedure?) . ->* . generator?) (generator () (let backtrack ([csp csp]) (match (select-unassigned-variable csp) @@ -124,28 +144,34 @@ [csp (inference csp name)]) (backtrack csp))))])))) -(define/contract (solve* csp [finish-proc $csp-vars][solution-limit +inf.0]) - (($csp?) (procedure? integer?) . ->* . (listof any/c)) - (for/list ([solution (in-producer (backtrack-solution-generator csp) (void))] - [idx (in-range solution-limit)]) +(define/contract (solve* csp + #:finish-proc [finish-proc $csp-vars] + #:solver [solver (or (current-solver) backtracking-solver)] + #:count [max-solutions +inf.0]) + (($csp?) (#:finish-proc procedure? #:solver generator? #:count integer?) . ->* . (listof any/c)) + (for/list ([solution (in-producer (solver csp) (void))] + [idx (in-range max-solutions)]) (finish-proc solution))) -(define/contract (solve csp [finish-proc $csp-vars]) - (($csp?) (procedure?) . ->* . (or/c #false any/c)) - (match (solve* csp finish-proc 1) +(define/contract (solve csp + #:finish-proc [finish-proc $csp-vars] + #:solver [solver (or (current-solver) backtracking-solver)]) + (($csp?) (#:finish-proc procedure? #:solver generator?) . ->* . (or/c #false any/c)) + (match (solve* csp #:finish-proc finish-proc #:solver solver #:count 1) [(list solution) solution] [else #false])) (define (<> a b) (not (= a b))) (define (neq? a b) (not (eq? a b))) -(parameterize ([current-inference forward-check]) - (time (solve* ($csp (list ($var 'a (range 3)) - ($var 'b (range 3)) - ($var 'c (range 3))) - (list ($constraint '(a b) <>) - ($constraint '(a c) <>) - ($constraint '(b c) <>)))))) +#;(parameterize ([current-inference forward-check]) + (time (solve* ($csp (list ($var 'a (range 3)) + ($var 'b (range 3)) + ($var 'c (range 3))) + (list ($constraint '(a b) <>) + ($constraint '(a c) <>) + ($constraint '(b c) <>)))))) + (parameterize ([current-inference forward-check]) (define vds (for/list ([k '(wa nsw t q nt v sa)]) ($var k '(red green blue))))