how do you start

main
Matthew Butterick 6 years ago
parent aba8901939
commit 5efc1406ff

@ -2,16 +2,118 @@
(require sugar "hacs.rkt")
(current-inference forward-check)
(current-select-variable mrv)
(current-select-variable mrv-degree-hybrid)
(current-order-values shuffle)
(current-shuffle #true)
(define xsum (make-csp))
(add-vars! xsum '(l1 l2 l3 l4 r1 r2 r3 r4 x) (range 1 10))
(add-pairwise-constraint! xsum < '(l1 l2 l3 l4))
(add-pairwise-constraint! xsum < '(r1 r2 r3 r4))
(add-constraint! xsum (λ (l1 l2 l3 l4 x) (= 27 (+ l1 l2 l3 l4 x))) '(l1 l2 l3 l4 x))
(add-constraint! xsum (λ (r1 r2 r3 r4 x) (= 27 (+ r1 r2 r3 r4 x))) '(r1 r2 r3 r4 x))
(add-pairwise-constraint! xsum alldiff= '(l1 l2 l3 l4 r1 r2 r3 r4 x))
(time (solve xsum))
#|
# There are no tricks, just pure logic, so good luck and don't give up.
#
# 1. In a street there are five houses, painted five different colours.
# 2. In each house lives a person of different nationality
# 3. These five homeowners each drink a different kind of beverage, smoke
# different brand of cigar and keep a different pet.
#
# THE QUESTION: WHO OWNS THE zebra?
#
# HINTS
#
# 1. The englishman lives in a red house.
# 2. The spaniard keeps dogs as pets.
# 5. The owner of the Green house drinks coffee.
# 3. The ukrainian drinks tea.
# 4. The Green house is on the left of the ivory house.
# 6. The person who smokes oldgold rears snails.
# 7. The owner of the Yellow house smokes kools.
# 8. The man living in the centre house drinks milk.
# 9. The Norwegian lives in the first house.
# 10. The man who smokes chesterfields lives next to the one who keeps foxes.
# 11. The man who keeps horses lives next to the man who smokes kools.
# 12. The man who smokes luckystrike drinks orangejuice.
# 13. The japanese smokes parliaments.
# 14. The Norwegian lives next to the blue house.
# 15. The man who smokes chesterfields has a neighbour who drinks water.
|#
(define (sym . args) (string->symbol (apply format args)))
(define zebra (make-csp))
(define ns (map (curry sym "nationality-~a") (range 5)))
(define cs (map (curry sym "color-~a") (range 5)))
(define ds (map (curry sym "drink-~a") (range 5)))
(define ss (map (curry sym "smoke-~a") (range 5)))
(define ps (map (curry sym "pet-~a") (range 5)))
(add-vars! zebra ns '(englishman spaniard ukrainian norwegian japanese))
(add-vars! zebra cs '(red ivory green yellow blue))
(add-vars! zebra ds '(tea coffee milk orange-juice water))
(add-vars! zebra ss '(oldgold kools chesterfields luckystrike parliaments))
(add-vars! zebra ps '(dogs snails foxes horses zebra))
(for ([vars (list ns cs ds ss ps)])
(add-pairwise-constraint! zebra neq? vars))
(define (paired-with lval left rval right)
(add-constraint! zebra (λ (left right) (or (not (eq? left lval)) (eq? rval right))) (list left right)))
(define (paired-with* lval lefts rval rights)
(for ([left lefts][right rights])
(paired-with lval left rval right)))
;# 1. The englishman lives in a red house.
('englishman ns . paired-with* . 'red cs)
;# 2. The spaniard keeps dogs as pets.
('spaniard ns . paired-with* . 'dogs ps)
;# 5. The owner of the Green house drinks coffee.
('green cs . paired-with* . 'coffee ds)
;# 3. The ukrainian drinks tea.
('ukrainian ns . paired-with* . 'tea ds)
;# 4. The Green house is on the left of the ivory house.
('green (drop-right cs 1) . paired-with* . 'ivory (drop cs 1))
(add-constraint! zebra (curry neq? 'ivory) (list 'color-0))
(add-constraint! zebra (curry neq? 'green) (list 'color-4))
;# 6. The person who smokes oldgold rears snails.
('oldgold ss . paired-with* . 'snails ps)
;# 7. The owner of the Yellow house smokes kools.
('yellow cs . paired-with* . 'kools ss)
;# 8. The man living in the centre house drinks milk.
(add-constraint! zebra (λ (d) (eq? d 'milk)) (list 'drink-2))
;# 9. The Norwegian lives in the first house.
(add-constraint! zebra (λ (x) (eq? x 'norwegian)) (list 'nationality-0))
(define (next-to lval lefts rval rights)
(lval (drop-right lefts 1) . paired-with* . rval (drop rights 1))
(lval (drop lefts 1) . paired-with* . rval (drop-right rights 1)))
;# 10. The man who smokes chesterfields lives next to the one who keeps foxes.
('chesterfields ss . next-to . 'foxes ps)
;# 11. The man who keeps horses lives next to the man who smokes kools.
;('horses ps . next-to . 'kools ss)
;# 12. The man who smokes luckystrike drinks orangejuice.
('luckystrike ss . paired-with* . 'orange-juice ds)
;# 13. The japanese smokes parliaments.
('japanese ns . paired-with* . 'parliaments ss)
;# 14. The Norwegian lives next to the blue house.
;('norwegian ns . next-to . 'water ds)
;# 15. The man who smokes chesterfields has a neighbour who drinks water.
;('chesterfields ss . next-to . 'water ds)
(define (finish x)
(apply map list (slice-at x 5)))
(map finish (list (time (solve zebra))))

@ -1,6 +1,10 @@
#lang debug racket
(require "hacs.rkt" rackunit)
(current-inference forward-check)
(current-select-variable mrv-degree-hybrid)
(current-order-values shuffle)
(current-shuffle #true)
(check-equal? (first-unassigned-variable ($csp (list ($var 'a (range 3)) ($var 'b (range 3))) null))
($var 'a (range 3)))
@ -78,5 +82,77 @@
'((dollars . 14) (quarters . 12)))
#;(check-equal? (length (time (solve* xsum))) 8)
;; xsum
#|
# Reorganize the following numbers in a way that each line of
# 5 numbers sum to 27.
#
# 1 6
# 2 7
# 3
# 8 4
# 9 5
#
|#
(define xsum (make-csp))
(add-vars! xsum '(l1 l2 l3 l4 r1 r2 r3 r4 x) (range 1 10))
(add-pairwise-constraint! xsum < '(l1 l2 l3 l4))
(add-pairwise-constraint! xsum < '(r1 r2 r3 r4))
(add-constraint! xsum (λ (l1 l2 l3 l4 x) (= 27 (+ l1 l2 l3 l4 x))) '(l1 l2 l3 l4 x))
(add-constraint! xsum (λ (r1 r2 r3 r4 x) (= 27 (+ r1 r2 r3 r4 x))) '(r1 r2 r3 r4 x))
(add-pairwise-constraint! xsum alldiff= '(l1 l2 l3 l4 r1 r2 r3 r4 x))
(check-equal? (length (time (solve* xsum))) 8)
;; send more money problem
#|
# Assign equal values to equal letters, and different values to
# different letters, in a way that satisfies the following sum:
#
# SEND
# + MORE
# ------
# MONEY
|#
(define (word-value . xs)
(for/sum ([(x idx) (in-indexed (reverse xs))])
(* x (expt 10 idx))))
(define smm (make-csp))
(add-vars! smm '(s e n d m o r y) (λ () (range 10)))
(add-constraint! smm positive? '(s))
(add-constraint! smm positive? '(m))
(add-constraint! smm (λ (d e y) (= (modulo (+ d e) 10) y)) '(d e y))
(add-constraint! smm (λ (n d r e y)
(= (modulo (+ (word-value n d) (word-value r e)) 100)
(word-value e y))) '(n d r e y))
(add-constraint! smm (λ (e n d o r y)
(= (modulo (+ (word-value e n d) (word-value o r e)) 1000) (word-value n e y))) '(e n d o r y))
(add-constraint! smm (λ (s e n d m o r y)
(= (+ (word-value s e n d) (word-value m o r e))
(word-value m o n e y))) '(s e n d m o r y))
(add-pairwise-constraint! smm alldiff= '(s e n d m o r y))
(check-equal? (time (solve smm)) '((s . 9) (e . 5) (n . 6) (d . 7) (m . 1) (o . 0) (r . 8) (y . 2)))
;; queens problem
;; place queens on chessboard so they do not intersect
(define queens (make-csp))
(define qs (for/list ([q 8]) (string->symbol (format "q~a" q))))
(define rows (range (length qs)))
(add-vars! queens qs rows)
(define (q-col q) (string->number (string-trim (symbol->string q) "q")))
(for* ([qs (in-combinations qs 2)])
(match-define (list qa qb) qs)
(match-define (list qa-col qb-col) (map q-col qs))
(add-constraint! queens
(λ (qa-row qb-row)
(and
(not (= (abs (- qa-row qb-row)) (abs (- qa-col qb-col)))) ; same diagonal?
(not (= qa-row qb-row)))) ; same row?
(list qa qb)))
(check-equal? 92 (length (time (solve* queens))))

@ -8,7 +8,7 @@
(if (null? argss)
(yield (reverse acc))
(for ([arg (in-list (car argss))])
(loop (cdr argss) (cons arg acc))))))))
(loop (cdr argss) (cons arg acc))))))))
(struct $csp ([vars #:mutable]
[constraints #:mutable]) #:transparent)
@ -19,7 +19,7 @@
(raise-argument-error '$constraint-proc "$csp" csp))
;; apply proc in many-to-many style
(for/and ([args (in-cartesian (map (λ (cname) ($csp-vals csp cname)) ($constraint-names constraint)))])
(apply ($constraint-proc constraint) args))))
(apply ($constraint-proc constraint) args))))
(struct $var (name domain) #:transparent)
(define $var-name? symbol?)
@ -28,7 +28,6 @@
(struct $cvar $var (past) #:transparent)
(struct $avar $var () #:transparent)
(define (make-csp [vds null] [constraints null])
($csp vds constraints))
@ -54,11 +53,11 @@
(($csp? procedure? (listof (listof $var-name?))) ((or/c #false $var-name?)) . ->* . void?)
(set-$csp-constraints! csp (append ($csp-constraints csp)
(for/list ([names (in-list namess)])
(for ([name (in-list names)])
(check-name-in-csp! 'add-constraints! csp name))
($constraint names (if proc-name
(procedure-rename proc proc-name)
proc))))))
(for ([name (in-list names)])
(check-name-in-csp! 'add-constraints! csp name))
($constraint names (if proc-name
(procedure-rename proc proc-name)
proc))))))
(define/contract (add-pairwise-constraint! csp proc var-names [proc-name #false])
(($csp? procedure? (listof $var-name?)) ($var-name?) . ->* . void?)
@ -93,7 +92,7 @@
(check-name-in-csp! '$csp-var csp name)
(for/first ([var (in-list ($csp-vars csp))]
#:when (eq? name ($var-name var)))
var))
var))
(define/contract ($csp-vals csp name)
($csp? $var-name? . -> . (listof any/c))
@ -135,34 +134,34 @@
(ormap assigned-name? ($constraint-names constraint)))
($csp ($csp-vars csp)
(for/list ([constraint (in-list ($csp-constraints csp))])
(cond
[(and (if minimum-arity (<= minimum-arity (constraint-arity constraint)) #true)
(partially-assigned? constraint))
(match-define ($constraint cnames proc) constraint)
($constraint (filter-not assigned-name? cnames)
;; pattern is mix of values and symbols (indicating variables to persist)
(let ([reduce-arity-pattern (for/list ([cname (in-list cnames)])
(if (assigned-name? cname)
(first ($csp-vals csp cname))
cname))])
(reduce-arity proc reduce-arity-pattern)))]
[else constraint])))))
(cond
[(and (if minimum-arity (<= minimum-arity (constraint-arity constraint)) #true)
(partially-assigned? constraint))
(match-define ($constraint cnames proc) constraint)
($constraint (filter-not assigned-name? cnames)
;; pattern is mix of values and symbols (indicating variables to persist)
(let ([reduce-arity-pattern (for/list ([cname (in-list cnames)])
(if (assigned-name? cname)
(first ($csp-vals csp cname))
cname))])
(reduce-arity proc reduce-arity-pattern)))]
[else constraint])))))
(define/contract (assign-val csp name val)
($csp? $var-name? any/c . -> . $csp?)
(define assigned-csp ($csp
(for/list ([var ($csp-vars csp)])
(if (eq? name ($var-name var))
($avar name (list val))
var))
(if (eq? name ($var-name var))
($avar name (list val))
var))
($csp-constraints csp)))
(reduce-constraint-arity assigned-csp))
assigned-csp)
(define/contract (unassigned-vars csp)
($csp? . -> . (listof (and/c $var? (not/c $avar?))))
(for/list ([var (in-list ($csp-vars csp))]
#:unless ($avar? var))
var))
var))
(define/contract (first-unassigned-variable csp)
($csp? . -> . (or/c #false (and/c $var? (not/c $avar?))))
@ -188,7 +187,7 @@
($csp? $var? . -> . exact-nonnegative-integer?)
(for/sum ([constraint (in-list ($csp-constraints csp))]
#:when (memq ($var-name var) ($constraint-names constraint)))
1))
1))
(define/contract (blended-variable-selector csp)
($csp? . -> . (or/c #false (and/c $var? (not/c $avar?))))
@ -199,6 +198,26 @@
[else (first (let* ([uvars-by-mrv (sort uvars < #:key (λ (var) (length ($var-domain var))))]
[uvars-by-degree (sort uvars-by-mrv > #:key (λ (var) (var-degree csp var)))])
uvars-by-degree))]))
(define/contract (remaining-values var)
($var? . -> . exact-nonnegative-integer?)
(length ($var-vals var)))
(define/contract (mrv-degree-hybrid csp)
($csp? . -> . (or/c #f $var?))
(define uvars (unassigned-vars csp))
(cond
[(empty? uvars) #false]
[else
;; minimum remaining values (MRV) rule
(define mrv-arg (argmin remaining-values uvars))
(match (filter (λ (var) (= (remaining-values mrv-arg) (remaining-values var))) uvars)
[(list winning-uvar) winning-uvar]
[(list mrv-uvars ...)
;; use degree as tiebreaker for mrv
(define max-degree-arg (argmax (λ (var) (var-degree csp var)) mrv-uvars))
;; use random tiebreaker for degree
(first (shuffle (filter (λ (var) (= (var-degree csp max-degree-arg) (var-degree csp var))) mrv-uvars)))])]))
(define first-domain-value values)
@ -210,8 +229,8 @@
[cnames (in-value ($constraint-names constraint))]
#:when (and (= (length names) (length cnames))
(for/and ([name (in-list names)])
(memq name cnames))))
constraint))
(memq name cnames))))
constraint))
(define/contract (forward-check csp aname)
($csp? $var-name? . -> . $csp?)
@ -228,11 +247,11 @@
(define new-vals
(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))
(let ([proc ($constraint-proc constraint)])
(if (eq? name (first ($constraint-names constraint)))
(proc val aval)
(proc aval val)))))
val))
($cvar name new-vals (cons aname (if ($cvar? var)
($cvar-past var)
null)))])]))
@ -241,7 +260,7 @@
(define conflict-set (for*/list ([var (in-list checked-vars)]
#:when (empty? ($var-domain var))
[name (in-list ($cvar-past var))])
name))
name))
;; for conflict-directed backjumping it's essential to forward-check ALL vars
;; (even after an empty domain is generated) and combine their conflicts
;; so we can discover the *most recent past var* that could be the culprit.
@ -253,18 +272,18 @@
;; (they have no further use)
(define nonsingleton-constraints
(for/list ([constraint (in-list ($csp-constraints csp))]
#:unless (and
(= 2 (constraint-arity constraint)) ; binary constraint
(memq aname ($constraint-names constraint)) ; includes target name
(let ([other-name (first (remq aname ($constraint-names constraint)))]) ; and something else
(= (length ($csp-vals csp other-name)) 1)))) ; that has only one value
constraint))
#:unless (and
(= 2 (constraint-arity constraint)) ; binary constraint
(memq aname ($constraint-names constraint)) ; includes target name
(let ([other-name (first (remq aname ($constraint-names constraint)))]) ; and something else
(= (length ($csp-vals csp other-name)) 1)))) ; that has only one value
constraint))
($csp checked-vars nonsingleton-constraints))
(define/contract (constraint-checkable? c names)
($constraint? (listof $var-name?) . -> . boolean?)
(and (for/and ([cname (in-list ($constraint-names c))])
(memq cname names))
(memq cname names))
#true))
(define/contract (constraint-arity constraint)
@ -281,14 +300,30 @@
;; we also want to use "singleton" vars (that is, vars that have been reduced to a single domain value by forward checking)
(define singleton-varnames (for/list ([var (in-list ($csp-vars csp))]
#:when (singleton-var? var))
($var-name var)))
($var-name var)))
(define-values (checkable-constraints other-constraints)
(partition (λ (c) (constraint-checkable? c singleton-varnames)) ($csp-constraints csp)))
(for ([constraint (in-list (sort checkable-constraints < #:key constraint-arity))]
#:unless (constraint csp))
(raise ($backtrack null)))
(raise ($backtrack null)))
($csp ($csp-vars csp) other-constraints))
(define/contract (make-nodes-consistent csp)
($csp? . -> . $csp?)
;; todo: why does this function make searches so much slower?
($csp
(for/list ([var (in-list ($csp-vars csp))])
(match-define ($var name vals) var)
(define procs (for*/list ([constraint (in-list ($csp-constraints csp))]
[cnames (in-value ($constraint-names constraint))]
#:when (and (= 1 (length cnames)) (eq? name (car cnames))))
($constraint-proc constraint)))
($var name
(for*/fold ([vals vals])
([proc (in-list procs)])
(filter proc vals))))
($csp-constraints csp)))
(define/contract (backtracking-solver
csp
#:select-variable [select-unassigned-variable
@ -310,18 +345,19 @@
(with-handlers ([wants-backtrack?
(λ (bt) (append conflicts (remq name ($backtrack-names bt))))])
(let* ([csp (assign-val csp name val)]
;; reduce constraints before inference,
;; to create more forward-checkable (binary) constraints
[csp (reduce-constraint-arity csp)]
[csp (inference csp name)]
[csp (check-constraints csp)])
(loop csp)))
conflicts)]))))
(define/contract ($csp-assocs csp)
($csp? . -> . (listof (cons/c $var-name? any/c)))
(for/list ([var (in-list ($csp-vars csp))])
(match var
[($var name domain) (cons name (first domain))])))
(match var
[($var name domain) (cons name (first domain))])))
(define/contract (solve* csp
#:finish-proc [finish-proc $csp-assocs]
@ -330,7 +366,7 @@
(($csp?) (#:finish-proc procedure? #:solver procedure? #:count integer?) . ->* . (listof any/c))
(for/list ([solution (in-producer (solver csp) (void))]
[idx (in-range max-solutions)])
(finish-proc solution)))
(finish-proc solution)))
(define/contract (solve csp
#:finish-proc [finish-proc $csp-assocs]

@ -1,60 +0,0 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# PyCharm / Intellij
.idea/

@ -1,21 +0,0 @@
language: python
python:
- "2.7"
- "3.3"
- "3.4"
- "3.5"
- "3.6"
# command to install dependencies
install:
- "pip install -qq flake8"
- "pip install coveralls --quiet"
- "pip install ."
# command to run tests
script:
- nosetests -s -v --with-coverage --cover-package=constraint
- flake8 --ignore E501 constraint examples tests
after_success:
- coveralls

@ -1,23 +0,0 @@
Copyright (c) 2005-2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,159 +0,0 @@
|Build Status| |Code Health| |Code Coverage|
python-constraint
=================
Introduction
------------
The Python constraint module offers solvers for `Constraint Satisfaction Problems (CSPs) <https://en.wikipedia.org/wiki/Constraint_satisfaction_problem>`_ over finite domains in simple and pure Python. CSP is class of problems which may be represented in terms of variables (a, b, ...), domains (a in [1, 2, 3], ...), and constraints (a < b, ...).
Examples
--------
Basics
~~~~~~
This interactive Python session demonstrates the module basic operation:
.. code-block:: python
>>> from constraint import *
>>> problem = Problem()
>>> problem.addVariable("a", [1,2,3])
>>> problem.addVariable("b", [4,5,6])
>>> problem.getSolutions()
[{'a': 3, 'b': 6}, {'a': 3, 'b': 5}, {'a': 3, 'b': 4},
{'a': 2, 'b': 6}, {'a': 2, 'b': 5}, {'a': 2, 'b': 4},
{'a': 1, 'b': 6}, {'a': 1, 'b': 5}, {'a': 1, 'b': 4}]
>>> problem.addConstraint(lambda a, b: a*2 == b,
("a", "b"))
>>> problem.getSolutions()
[{'a': 3, 'b': 6}, {'a': 2, 'b': 4}]
>>> problem = Problem()
>>> problem.addVariables(["a", "b"], [1, 2, 3])
>>> problem.addConstraint(AllDifferentConstraint())
>>> problem.getSolutions()
[{'a': 3, 'b': 2}, {'a': 3, 'b': 1}, {'a': 2, 'b': 3},
{'a': 2, 'b': 1}, {'a': 1, 'b': 2}, {'a': 1, 'b': 3}]
Rooks problem
~~~~~~~~~~~~~
The following example solves the classical Eight Rooks problem:
.. code-block:: python
>>> problem = Problem()
>>> numpieces = 8
>>> cols = range(numpieces)
>>> rows = range(numpieces)
>>> problem.addVariables(cols, rows)
>>> for col1 in cols:
... for col2 in cols:
... if col1 < col2:
... problem.addConstraint(lambda row1, row2: row1 != row2,
... (col1, col2))
>>> solutions = problem.getSolutions()
>>> solutions
>>> solutions
[{0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 1, 7: 0},
{0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 2, 6: 0, 7: 1},
{0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 1, 6: 2, 7: 0},
{0: 7, 1: 6, 2: 5, 3: 4, 4: 3, 5: 1, 6: 0, 7: 2},
...
{0: 7, 1: 5, 2: 3, 3: 6, 4: 2, 5: 1, 6: 4, 7: 0},
{0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 2, 6: 0, 7: 4},
{0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 2, 6: 4, 7: 0},
{0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 4, 6: 2, 7: 0},
{0: 7, 1: 5, 2: 3, 3: 6, 4: 1, 5: 4, 6: 0, 7: 2},
...]
Magic squares
~~~~~~~~~~~~~
This example solves a 4x4 magic square:
.. code-block:: python
>>> problem = Problem()
>>> problem.addVariables(range(0, 16), range(1, 16 + 1))
>>> problem.addConstraint(AllDifferentConstraint(), range(0, 16))
>>> problem.addConstraint(ExactSumConstraint(34), [0, 5, 10, 15])
>>> problem.addConstraint(ExactSumConstraint(34), [3, 6, 9, 12])
>>> for row in range(4):
... problem.addConstraint(ExactSumConstraint(34),
[row * 4 + i for i in range(4)])
>>> for col in range(4):
... problem.addConstraint(ExactSumConstraint(34),
[col + 4 * i for i in range(4)])
>>> solutions = problem.getSolutions()
Features
--------
The following solvers are available:
- Backtracking solver
- Recursive backtracking solver
- Minimum conflicts solver
.. role:: python(code)
:language: python
Predefined constraint types currently available:
- :python:`FunctionConstraint`
- :python:`AllDifferentConstraint`
- :python:`AllEqualConstraint`
- :python:`ExactSumConstraint`
- :python:`MaxSumConstraint`
- :python:`MinSumConstraint`
- :python:`InSetConstraint`
- :python:`NotInSetConstraint`
- :python:`SomeInSetConstraint`
- :python:`SomeNotInSetConstraint`
API documentation
-----------------
Documentation for the module is available at: http://labix.org/doc/constraint/
Download and install
--------------------
.. code-block:: shell
$ pip install python-constraint
Roadmap
-------
This GitHub organization and repository is a global effort to help to
maintain python-constraint which was written by Gustavo Niemeyer
and originaly located at https://labix.org/python-constraint
- Create some unit tests - DONE
- Enable continuous integration - DONE
- Port to Python 3 (Python 2 being also supported) - DONE
- Respect Style Guide for Python Code (PEP8) - DONE
- Improve code coverage writting more unit tests - ToDo
- Move doc to Sphinx or MkDocs - https://readthedocs.org/ - ToDo
Contact
-------
- `Gustavo Niemeyer <https://github.com/niemeyer/>`_ <gustavo@niemeyer.net>
- `Sébastien Celles <https://github.com/scls19fr/>`_ <s.celles@gmail.com>
But it's probably better to `open an issue <https://github.com/python-constraint/python-constraint/issues>`_.
.. |Build Status| image:: https://travis-ci.org/python-constraint/python-constraint.svg?branch=master
:target: https://travis-ci.org/python-constraint/python-constraint
.. |Code Health| image:: https://landscape.io/github/python-constraint/python-constraint/master/landscape.svg?style=flat
:target: https://landscape.io/github/python-constraint/python-constraint/master
:alt: Code Health
.. |Code Coverage| image:: https://coveralls.io/repos/github/python-constraint/python-constraint/badge.svg
:target: https://coveralls.io/github/python-constraint/python-constraint

File diff suppressed because it is too large Load Diff

@ -1,14 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
PY2 = sys.version_info[0] == 2
PY3 = (sys.version_info[0] >= 3)
if PY3:
string_types = str
xrange = range
else:
string_types = basestring # noqa
xrange = xrange

@ -1,8 +0,0 @@
__author__ = "Gustavo Niemeyer"
__copyright__ = "Copyright (c) 2005-2014 - Gustavo Niemeyer <gustavo@niemeyer.net>"
__credits__ = ["Sebastien Celles"]
__license__ = ""
__version__ = "1.3.1"
__email__ = "gustavo@niemeyer.net"
__status__ = "Development"
__url__ = 'https://github.com/python-constraint/python-constraint'

@ -1,37 +0,0 @@
#!/usr/bin/python
#
# What's the minimum value for:
#
# ABC
# -------
# A+B+C
#
# From http://www.umassd.edu/mathcontest/abc.cfm
#
from constraint import Problem
def solve():
problem = Problem()
problem.addVariables("abc", range(1, 10))
problem.getSolutions()
minvalue = 999 / (9 * 3)
minsolution = {}
for solution in problem.getSolutions():
a = solution["a"]
b = solution["b"]
c = solution["c"]
value = (a * 100 + b * 10 + c) / (a + b + c)
if value < minvalue:
minsolution = solution
return minvalue, minsolution
def main():
minvalue, minsolution = solve()
print(minvalue)
print(minsolution)
if __name__ == "__main__":
main()

@ -1,36 +0,0 @@
#!/usr/bin/python
#
# 100 coins must sum to $5.00
#
# That's kind of a country-specific problem, since depending on the
# country there are different values for coins. Here is presented
# the solution for a given set.
#
from constraint import Problem, ExactSumConstraint
import sys
def solve():
problem = Problem()
total = 5.00
variables = ("0.01", "0.05", "0.10", "0.50", "1.00")
values = [float(x) for x in variables]
for variable, value in zip(variables, values):
problem.addVariable(variable, range(int(total / value)))
problem.addConstraint(ExactSumConstraint(total, values), variables)
problem.addConstraint(ExactSumConstraint(100))
solutions = problem.getSolutionIter()
return solutions, variables
def main():
solutions, variables = solve()
for i, solution in enumerate(solutions):
sys.stdout.write("%03d -> " % (i + 1))
for variable in variables:
sys.stdout.write("%s:%d " % (variable, solution[variable]))
sys.stdout.write("\n")
if __name__ == "__main__":
main()

@ -1,154 +0,0 @@
#!/usr/bin/python
from constraint import Problem, AllDifferentConstraint
import random
import sys
MINLEN = 3
def main(puzzle, lines):
puzzle = puzzle.rstrip().splitlines()
while puzzle and not puzzle[0]:
del puzzle[0]
# Extract horizontal words
horizontal = []
word = []
predefined = {}
for row in range(len(puzzle)):
for col in range(len(puzzle[row])):
char = puzzle[row][col]
if not char.isspace():
word.append((row, col))
if char != "#":
predefined[row, col] = char
elif word:
if len(word) > MINLEN:
horizontal.append(word[:])
del word[:]
if word:
if len(word) > MINLEN:
horizontal.append(word[:])
del word[:]
# Extract vertical words
vertical = []
validcol = True
col = 0
while validcol:
validcol = False
for row in range(len(puzzle)):
if col >= len(puzzle[row]):
if word:
if len(word) > MINLEN:
vertical.append(word[:])
del word[:]
else:
validcol = True
char = puzzle[row][col]
if not char.isspace():
word.append((row, col))
if char != "#":
predefined[row, col] = char
elif word:
if len(word) > MINLEN:
vertical.append(word[:])
del word[:]
if word:
if len(word) > MINLEN:
vertical.append(word[:])
del word[:]
col += 1
# hnames = ["h%d" % i for i in range(len(horizontal))]
# vnames = ["v%d" % i for i in range(len(vertical))]
# problem = Problem(MinConflictsSolver())
problem = Problem()
for hi, hword in enumerate(horizontal):
for vi, vword in enumerate(vertical):
for hchar in hword:
if hchar in vword:
hci = hword.index(hchar)
vci = vword.index(hchar)
problem.addConstraint(lambda hw, vw, hci=hci, vci=vci:
hw[hci] == vw[vci],
("h%d" % hi, "v%d" % vi))
for char, letter in predefined.items():
for hi, hword in enumerate(horizontal):
if char in hword:
hci = hword.index(char)
problem.addConstraint(lambda hw, hci=hci, letter=letter:
hw[hci] == letter, ("h%d" % hi,))
for vi, vword in enumerate(vertical):
if char in vword:
vci = vword.index(char)
problem.addConstraint(lambda vw, vci=vci, letter=letter:
vw[vci] == letter, ("v%d" % vi,))
wordsbylen = {}
for hword in horizontal:
wordsbylen[len(hword)] = []
for vword in vertical:
wordsbylen[len(vword)] = []
for line in lines:
line = line.strip()
ll = len(line)
if ll in wordsbylen:
wordsbylen[ll].append(line.upper())
for hi, hword in enumerate(horizontal):
words = wordsbylen[len(hword)]
random.shuffle(words)
problem.addVariable("h%d" % hi, words)
for vi, vword in enumerate(vertical):
words = wordsbylen[len(vword)]
random.shuffle(words)
problem.addVariable("v%d" % vi, words)
problem.addConstraint(AllDifferentConstraint())
solution = problem.getSolution()
if not solution:
print("No solution found!")
maxcol = 0
maxrow = 0
for hword in horizontal:
for row, col in hword:
if row > maxrow:
maxrow = row
if col > maxcol:
maxcol = col
for vword in vertical:
for row, col in vword:
if row > maxrow:
maxrow = row
if col > maxcol:
maxcol = col
matrix = []
for row in range(maxrow + 1):
matrix.append([" "] * (maxcol + 1))
for variable in solution:
if variable[0] == "v":
word = vertical[int(variable[1:])]
else:
word = horizontal[int(variable[1:])]
for (row, col), char in zip(word, solution[variable]):
matrix[row][col] = char
for row in range(maxrow + 1):
for col in range(maxcol + 1):
sys.stdout.write(matrix[row][col])
sys.stdout.write("\n")
if __name__ == "__main__":
if len(sys.argv) != 3:
sys.exit("Usage: crosswords.py <maskfile> <wordsfile>")
main(open(sys.argv[1]).read(), open(sys.argv[2]))

@ -1,27 +0,0 @@
# ######## #
# # # # #
######## # #
# # # # #
# # ########
# # # # # #
######## # #
# # # # # #
# # #
######## # #
# # # # #
# ########
# # # # #
# # ########
# # # # # #
# # ########
# # # #
######## # #
# # # # # #
# # # # # #
######## # #
# # # #
# ########
# # # #
######## # #

@ -1,19 +0,0 @@
#
#########
# # #
# # ######
# # #
# # # #
# # # #
######## #
# # #
# # #
#########
# # #
#########
# # #
# #
#######
#

@ -1,8 +0,0 @@
P
Y
####T####
# H #
# O #
####N #
# #
#########

@ -1,8 +0,0 @@
#
#
#########
# #
# # # #
##### # #
# # #
#########

@ -1,209 +0,0 @@
#!/usr/bin/python
#
# ALBERT EINSTEIN'S RIDDLE
#
# ARE YOU IN THE TOP 2% OF INTELLIGENT PEOPLE IN THE WORLD?
# SOLVE THE RIDDLE AND FIND OUT.
#
# There are no tricks, just pure logic, so good luck and don't give up.
#
# 1. In a street there are five houses, painted five different colours.
# 2. In each house lives a person of different nationality
# 3. These five homeowners each drink a different kind of beverage, smoke
# different brand of cigar and keep a different pet.
#
# THE QUESTION: WHO OWNS THE FISH?
#
# HINTS
#
# 1. The Brit lives in a red house.
# 2. The Swede keeps dogs as pets.
# 3. The Dane drinks tea.
# 4. The Green house is on the left of the White house.
# 5. The owner of the Green house drinks coffee.
# 6. The person who smokes Pall Mall rears birds.
# 7. The owner of the Yellow house smokes Dunhill.
# 8. The man living in the centre house drinks milk.
# 9. The Norwegian lives in the first house.
# 10. The man who smokes Blends lives next to the one who keeps cats.
# 11. The man who keeps horses lives next to the man who smokes Dunhill.
# 12. The man who smokes Blue Master drinks beer.
# 13. The German smokes Prince.
# 14. The Norwegian lives next to the blue house.
# 15. The man who smokes Blends has a neighbour who drinks water.
#
# ALBERT EINSTEIN WROTE THIS RIDDLE EARLY DURING THE 19th CENTURY. HE
# SAID THAT 98% OF THE WORLD POPULATION WOULD NOT BE ABLE TO SOLVE IT.
from constraint import Problem, AllDifferentConstraint
# Check http://www.csc.fi/oppaat/f95/python/talot.py
def solve():
problem = Problem()
for i in range(1, 6):
problem.addVariable("color%d" % i,
["red", "white", "green", "yellow", "blue"])
problem.addVariable("nationality%d" % i,
["brit", "swede", "dane", "norwegian", "german"])
problem.addVariable("drink%d" % i,
["tea", "coffee", "milk", "beer", "water"])
problem.addVariable("smoke%d" % i,
["pallmall", "dunhill", "blends",
"bluemaster", "prince"])
problem.addVariable("pet%d" % i,
["dogs", "birds", "cats", "horses", "fish"])
problem.addConstraint(AllDifferentConstraint(),
["color%d" % i for i in range(1, 6)])
problem.addConstraint(AllDifferentConstraint(),
["nationality%d" % i for i in range(1, 6)])
problem.addConstraint(AllDifferentConstraint(),
["drink%d" % i for i in range(1, 6)])
problem.addConstraint(AllDifferentConstraint(),
["smoke%d" % i for i in range(1, 6)])
problem.addConstraint(AllDifferentConstraint(),
["pet%d" % i for i in range(1, 6)])
for i in range(1, 6):
# Hint 1
problem.addConstraint(lambda nationality, color:
nationality != "brit" or color == "red",
("nationality%d" % i, "color%d" % i))
# Hint 2
problem.addConstraint(lambda nationality, pet:
nationality != "swede" or pet == "dogs",
("nationality%d" % i, "pet%d" % i))
# Hint 3
problem.addConstraint(lambda nationality, drink:
nationality != "dane" or drink == "tea",
("nationality%d" % i, "drink%d" % i))
# Hint 4
if i < 5:
problem.addConstraint(lambda colora, colorb:
colora != "green" or colorb == "white",
("color%d" % i, "color%d" % (i + 1)))
else:
problem.addConstraint(lambda color: color != "green",
("color%d" % i,))
# Hint 5
problem.addConstraint(lambda color, drink:
color != "green" or drink == "coffee",
("color%d" % i, "drink%d" % i))
# Hint 6
problem.addConstraint(lambda smoke, pet:
smoke != "pallmall" or pet == "birds",
("smoke%d" % i, "pet%d" % i))
# Hint 7
problem.addConstraint(lambda color, smoke:
color != "yellow" or smoke == "dunhill",
("color%d" % i, "smoke%d" % i))
# Hint 8
if i == 3:
problem.addConstraint(lambda drink: drink == "milk",
("drink%d" % i,))
# Hint 9
if i == 1:
problem.addConstraint(lambda nationality:
nationality == "norwegian",
("nationality%d" % i,))
# Hint 10
if 1 < i < 5:
problem.addConstraint(lambda smoke, peta, petb:
smoke != "blends" or peta == "cats" or
petb == "cats",
("smoke%d" % i, "pet%d" % (i - 1),
"pet%d" % (i + 1)))
else:
problem.addConstraint(lambda smoke, pet:
smoke != "blends" or pet == "cats",
("smoke%d" % i,
"pet%d" % (i == 1 and 2 or 4)))
# Hint 11
if 1 < i < 5:
problem.addConstraint(lambda pet, smokea, smokeb:
pet != "horses" or smokea == "dunhill" or
smokeb == "dunhill",
("pet%d" % i, "smoke%d" % (i - 1),
"smoke%d" % (i + 1)))
else:
problem.addConstraint(lambda pet, smoke:
pet != "horses" or smoke == "dunhill",
("pet%d" % i,
"smoke%d" % (i == 1 and 2 or 4)))
# Hint 12
problem.addConstraint(lambda smoke, drink:
smoke != "bluemaster" or drink == "beer",
("smoke%d" % i, "drink%d" % i))
# Hint 13
problem.addConstraint(lambda nationality, smoke:
nationality != "german" or smoke == "prince",
("nationality%d" % i, "smoke%d" % i))
# Hint 14
if 1 < i < 5:
problem.addConstraint(lambda nationality, colora, colorb:
nationality != "norwegian" or
colora == "blue" or colorb == "blue",
("nationality%d" % i, "color%d" % (i - 1),
"color%d" % (i + 1)))
else:
problem.addConstraint(lambda nationality, color:
nationality != "norwegian" or
color == "blue",
("nationality%d" % i,
"color%d" % (i == 1 and 2 or 4)))
# Hint 15
if 1 < i < 5:
problem.addConstraint(lambda smoke, drinka, drinkb:
smoke != "blends" or
drinka == "water" or drinkb == "water",
("smoke%d" % i, "drink%d" % (i - 1),
"drink%d" % (i + 1)))
else:
problem.addConstraint(lambda smoke, drink:
smoke != "blends" or drink == "water",
("smoke%d" % i,
"drink%d" % (i == 1 and 2 or 4)))
solutions = problem.getSolutions()
return solutions
def showSolution(solution):
for i in range(1, 6):
print("House %d" % i)
print("--------")
print("Nationality: %s" % solution["nationality%d" % i])
print("Color: %s" % solution["color%d" % i])
print("Drink: %s" % solution["drink%d" % i])
print("Smoke: %s" % solution["smoke%d" % i])
print("Pet: %s" % solution["pet%d" % i])
print("")
def main():
solutions = solve()
print("Found %d solution(s)!" % len(solutions))
print("")
for solution in solutions:
showSolution(solution)
if __name__ == "__main__":
main()

@ -1,54 +0,0 @@
#!/usr/bin/python
#
# http://mathworld.wolfram.com/QueensProblem.html
#
from constraint import Problem
import sys
def solve():
problem = Problem()
size = 8
cols = range(size)
rows = range(size)
problem.addVariables(cols, rows)
for col1 in cols:
for col2 in cols:
if col1 < col2:
problem.addConstraint(lambda row1, row2, col1=col1, col2=col2:
abs(row1 - row2) != abs(col1 - col2) and
row1 != row2, (col1, col2))
solutions = problem.getSolutions()
return solutions, size
def showSolution(solution, size):
sys.stdout.write(" %s \n" % ("-" * ((size * 4) - 1)))
for i in range(size):
sys.stdout.write(" |")
for j in range(size):
if solution[j] == i:
sys.stdout.write(" %d |" % j)
else:
sys.stdout.write(" |")
sys.stdout.write("\n")
if i != size - 1:
sys.stdout.write(" |%s|\n" % ("-" * ((size * 4) - 1)))
sys.stdout.write(" %s \n" % ("-" * ((size * 4) - 1)))
def main(show=False):
solutions, size = solve()
print("Found %d solution(s)!" % len(solutions))
if show:
for solution in solutions:
showSolution(solution, size)
if __name__ == "__main__":
show = False
if len(sys.argv) == 2 and sys.argv[1] == "-s":
show = True
elif len(sys.argv) != 1:
sys.exit("Usage: queens.py [-s]")
main(show)

@ -1,57 +0,0 @@
#!/usr/bin/python
#
# http://mathworld.wolfram.com/RooksProblem.html
#
from constraint import Problem
import sys
def factorial(x):
return x == 1 or factorial(x - 1) * x
def solve(size):
problem = Problem()
cols = range(size)
rows = range(size)
problem.addVariables(cols, rows)
for col1 in cols:
for col2 in cols:
if col1 < col2:
problem.addConstraint(lambda row1, row2: row1 != row2,
(col1, col2))
solutions = problem.getSolutions()
return solutions
def main(show=False):
size = 8
solutions = solve(size)
print("Found %d solution(s)!" % len(solutions))
if show:
for solution in solutions:
showSolution(solution, size)
def showSolution(solution, size):
sys.stdout.write(" %s \n" % ("-" * ((size * 4) - 1)))
for i in range(size):
sys.stdout.write(" |")
for j in range(size):
if solution[j] == i:
sys.stdout.write(" %d |" % j)
else:
sys.stdout.write(" |")
sys.stdout.write("\n")
if i != size - 1:
sys.stdout.write(" |%s|\n" % ("-" * ((size * 4) - 1)))
sys.stdout.write(" %s \n" % ("-" * ((size * 4) - 1)))
if __name__ == "__main__":
show = False
if len(sys.argv) == 2 and sys.argv[1] == "-s":
show = True
elif len(sys.argv) != 1:
sys.exit("Usage: rooks.py [-s]")
main(show)

@ -1,48 +0,0 @@
#!/usr/bin/python
#
# http://home.chello.no/~dudley/
#
from constraint import Problem, AllDifferentConstraint, SomeInSetConstraint
import sys
STUDENTDESKS = [[0, 1, 0, 0, 0, 0],
[0, 2, 3, 4, 5, 6],
[0, 7, 8, 9, 10, 0],
[0, 11, 12, 13, 14, 0],
[15, 16, 17, 18, 19, 0],
[0, 0, 0, 0, 20, 0]]
def solve():
problem = Problem()
problem.addVariables(range(1, 21), ["A", "B", "C", "D", "E"])
problem.addConstraint(SomeInSetConstraint(["A"], 4, True))
problem.addConstraint(SomeInSetConstraint(["B"], 4, True))
problem.addConstraint(SomeInSetConstraint(["C"], 4, True))
problem.addConstraint(SomeInSetConstraint(["D"], 4, True))
problem.addConstraint(SomeInSetConstraint(["E"], 4, True))
for row in range(len(STUDENTDESKS) - 1):
for col in range(len(STUDENTDESKS[row]) - 1):
lst = [STUDENTDESKS[row][col], STUDENTDESKS[row][col + 1],
STUDENTDESKS[row + 1][col], STUDENTDESKS[row + 1][col + 1]]
lst = [x for x in lst if x]
problem.addConstraint(AllDifferentConstraint(), lst)
solutions = problem.getSolution()
return solutions
def main():
solutions = solve()
showSolution(solutions)
def showSolution(solution):
for row in range(len(STUDENTDESKS)):
for col in range(len(STUDENTDESKS[row])):
id = STUDENTDESKS[row][col]
sys.stdout.write(" %s" % (id and solution[id] or " "))
sys.stdout.write("\n")
if __name__ == "__main__":
main()

@ -1,71 +0,0 @@
#
# Sudoku puzzle solver by by Luigi Poderico (www.poderico.it).
#
import sys
from constraint import Problem, AllDifferentConstraint
def solve():
problem = Problem()
# Define the variables: 9 rows of 9 variables rangin in 1...9
for i in range(1, 10):
problem.addVariables(range(i * 10 + 1, i * 10 + 10), range(1, 10))
# Each row has different values
for i in range(1, 10):
problem.addConstraint(AllDifferentConstraint(), range(i * 10 + 1, i * 10 + 10))
# Each colum has different values
for i in range(1, 10):
problem.addConstraint(AllDifferentConstraint(), range(10 + i, 100 + i, 10))
# Each 3x3 box has different values
problem.addConstraint(AllDifferentConstraint(), [11, 12, 13, 21, 22, 23, 31, 32, 33])
problem.addConstraint(AllDifferentConstraint(), [41, 42, 43, 51, 52, 53, 61, 62, 63])
problem.addConstraint(AllDifferentConstraint(), [71, 72, 73, 81, 82, 83, 91, 92, 93])
problem.addConstraint(AllDifferentConstraint(), [14, 15, 16, 24, 25, 26, 34, 35, 36])
problem.addConstraint(AllDifferentConstraint(), [44, 45, 46, 54, 55, 56, 64, 65, 66])
problem.addConstraint(AllDifferentConstraint(), [74, 75, 76, 84, 85, 86, 94, 95, 96])
problem.addConstraint(AllDifferentConstraint(), [17, 18, 19, 27, 28, 29, 37, 38, 39])
problem.addConstraint(AllDifferentConstraint(), [47, 48, 49, 57, 58, 59, 67, 68, 69])
problem.addConstraint(AllDifferentConstraint(), [77, 78, 79, 87, 88, 89, 97, 98, 99])
# Some value is given.
initValue = [[0, 9, 0, 7, 0, 0, 8, 6, 0],
[0, 3, 1, 0, 0, 5, 0, 2, 0],
[8, 0, 6, 0, 0, 0, 0, 0, 0],
[0, 0, 7, 0, 5, 0, 0, 0, 6],
[0, 0, 0, 3, 0, 7, 0, 0, 0],
[5, 0, 0, 0, 1, 0, 7, 0, 0],
[0, 0, 0, 0, 0, 0, 1, 0, 9],
[0, 2, 0, 6, 0, 0, 0, 5, 0],
[0, 5, 4, 0, 0, 8, 0, 7, 0]]
for i in range(1, 10):
for j in range(1, 10):
if initValue[i - 1][j - 1] != 0:
problem.addConstraint(lambda var, val=initValue[i - 1][j - 1]:
var == val, (i * 10 + j,))
# Get the solutions.
solutions = problem.getSolutions()
return solutions
def main():
solutions = solve()
# Print the solutions
for solution in solutions:
for i in range(1, 10):
for j in range(1, 10):
index = i * 10 + j
sys.stdout.write("%s " % solution[index])
print("")
print("")
if __name__ == "__main__":
main()

@ -1,39 +0,0 @@
#!/usr/bin/python
#
# Assign equal values to equal letters, and different values to
# different letters, in a way that satisfies the following sum:
#
# SEIS
# + SEIS
# ------
# DOZE
#
from constraint import Problem, AllDifferentConstraint
def solve():
problem = Problem()
problem.addVariables("seidoz", range(10))
problem.addConstraint(lambda s, e: (2 * s) % 10 == e, "se")
problem.addConstraint(lambda i, s, z, e: ((10 * 2 * i) + (2 * s)) % 100 ==
z * 10 + e, "isze")
problem.addConstraint(lambda s, e, i, d, o, z:
2 * (s * 1000 + e * 100 + i * 10 + s) ==
d * 1000 + o * 100 + z * 10 + e, "seidoz")
problem.addConstraint(lambda s: s != 0, "s")
problem.addConstraint(lambda d: d != 0, "d")
problem.addConstraint(AllDifferentConstraint())
solutions = problem.getSolutions()
return solutions
def main():
solutions = solve()
print("SEIS+SEIS=DOZE")
for s in solutions:
print("%(s)d%(e)d%(i)d%(s)s+%(s)d%(e)d%(i)d%(s)d="
"%(d)d%(o)d%(z)d%(e)d") % s
if __name__ == "__main__":
main()

@ -1,42 +0,0 @@
#!/usr/bin/python
#
# Assign equal values to equal letters, and different values to
# different letters, in a way that satisfies the following sum:
#
# SEND
# + MORE
# ------
# MONEY
#
from constraint import Problem, NotInSetConstraint, AllDifferentConstraint
def solve():
problem = Problem()
problem.addVariables("sendmory", range(10))
problem.addConstraint(lambda d, e, y: (d + e) % 10 == y, "dey")
problem.addConstraint(lambda n, d, r, e, y: (n * 10 + d + r * 10 + e) % 100 ==
e * 10 + y, "ndrey")
problem.addConstraint(lambda e, n, d, o, r, y:
(e * 100 + n * 10 + d + o * 100 + r * 10 + e) % 1000 ==
n * 100 + e * 10 + y, "endory")
problem.addConstraint(lambda s, e, n, d, m, o, r, y:
1000 * s + 100 * e + 10 * n + d + 1000 * m + 100 * o + 10 * r + e ==
10000 * m + 1000 * o + 100 * n + 10 * e + y, "sendmory")
problem.addConstraint(NotInSetConstraint([0]), "sm")
problem.addConstraint(AllDifferentConstraint())
solutions = problem.getSolutions()
return solutions
def main():
solutions = solve()
print("SEND+MORE=MONEY")
for s in solutions:
print("%(s)d%(e)d%(n)d%(d)d+"
"%(m)d%(o)d%(r)d%(e)d="
"%(m)d%(o)d%(n)d%(e)d%(y)d" % s)
if __name__ == "__main__":
main()

@ -1,37 +0,0 @@
#!/usr/bin/python
#
# Assign equal values to equal letters, and different values to
# different letters, in a way that satisfies the following sum:
#
# TWO
# + TWO
# -----
# FOUR
#
from constraint import Problem, AllDifferentConstraint, NotInSetConstraint
def solve():
problem = Problem()
problem.addVariables("twofur", range(10))
problem.addConstraint(lambda o, r: (2 * o) % 10 == r, "or")
problem.addConstraint(lambda w, o, u,
r: ((10 * 2 * w) + (2 * o)) % 100 == u * 10 + r, "wour")
problem.addConstraint(lambda t, w, o, f, u, r:
2 * (t * 100 + w * 10 + o) ==
f * 1000 + o * 100 + u * 10 + r, "twofur")
problem.addConstraint(NotInSetConstraint([0]), "ft")
problem.addConstraint(AllDifferentConstraint())
solutions = problem.getSolutions()
return solutions
def main():
solutions = solve()
print("TWO+TWO=FOUR")
for s in solutions:
print("%(t)d%(w)d%(o)d+%(t)d%(w)d%(o)d=%(f)d%(o)d%(u)d%(r)d" % s)
if __name__ == "__main__":
main()

@ -1,48 +0,0 @@
#!/usr/bin/python
#
# Reorganize the following numbers in a way that each line of
# 5 numbers sum to 27.
#
# 1 6
# 2 7
# 3
# 8 4
# 9 5
#
from constraint import Problem, AllDifferentConstraint
def solve():
problem = Problem()
problem.addVariables("abcdxefgh", range(1, 10))
problem.addConstraint(lambda a, b, c, d, x:
a < b < c < d and a + b + c + d + x == 27, "abcdx")
problem.addConstraint(lambda e, f, g, h, x:
e < f < g < h and e + f + g + h + x == 27, "efghx")
problem.addConstraint(AllDifferentConstraint())
solutions = problem.getSolutions()
return solutions
def main():
solutions = solve()
print("Found %d solutions!" % len(solutions))
showSolutions(solutions)
def showSolutions(solutions):
for solution in solutions:
print(""" %d %d
%d %d
%d
%d %d
%d %d
""" % (solution["a"], solution["e"],
solution["b"], solution["f"],
solution["x"],
solution["g"], solution["c"],
solution["h"], solution["d"]))
if __name__ == "__main__":
main()

@ -1,9 +0,0 @@
[bdist_wheel]
universal = 1
[bdist_rpm]
doc_files = README.rst
use_bzip2 = 1
[sdist]
formats = bztar

@ -1,123 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from setuptools import setup, find_packages # Always prefer setuptools over distutils
from codecs import open # To use a consistent encoding
from os import path
import io
NAME = 'python-constraint'
filename = "%s/version.py" % 'constraint'
with open(filename) as f:
exec(f.read())
here = path.abspath(path.dirname(__file__))
def readme():
filename = path.join(here, 'README.rst')
with io.open(filename, 'rt', encoding='UTF-8') as f:
return f.read()
setup(
name=NAME,
# Versions should comply with PEP440. For a discussion on single-sourcing
# the version across setup.py and the project code, see
# https://packaging.python.org/en/latest/development.html#single-sourcing-the-version
#version='0.0.1',
version=__version__,
description="python-constraint is a module implementing support "\
"for handling CSPs (Constraint Solving Problems) over finite domain",
long_description=readme(),
# The project's main homepage.
url=__url__,
# Author details
author=__author__,
author_email=__email__,
# Choose your license
license=__license__,
# See https://pypi.python.org/pypi?%3Aaction=list_classifiers
classifiers=[
# How mature is this project? Common values are
# 3 - Alpha
# 4 - Beta
# 5 - Production/Stable
'Development Status :: 3 - Alpha',
# Indicate who your project is intended for
'Environment :: Console',
#'Topic :: Software Development :: Build Tools',
'Intended Audience :: Science/Research',
'Operating System :: OS Independent',
# Specify the Python versions you support here. In particular, ensure
# that you indicate whether you support Python 2, Python 3 or both.
'Programming Language :: Cython',
'Programming Language :: Python',
#'Programming Language :: Python :: 2',
#'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
#'Programming Language :: Python :: 3',
#'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Topic :: Scientific/Engineering',
# Pick your license as you wish (should match "license" above)
'License :: OSI Approved :: BSD License',
],
# What does your project relate to?
keywords='csp constraint solving problems problem solver',
# You can just specify the packages manually here if your project is
# simple. Or you can use find_packages().
packages=find_packages(exclude=['contrib', 'docs', 'tests*']),
# List run-time dependencies here. These will be installed by pip when your
# project is installed. For an analysis of "install_requires" vs pip's
# requirements files see:
# https://packaging.python.org/en/latest/technical.html#install-requires-vs-requirements-files
install_requires=[],
# List additional groups of dependencies here (e.g. development dependencies).
# You can install these using the following syntax, for example:
# $ pip install -e .[dev,test]
extras_require = {
'dev': ['check-manifest', 'nose'],
'test': ['coverage', 'nose'],
},
# If there are data files included in your packages that need to be
# installed, specify them here. If using Python 2.6 or less, then these
# have to be included in MANIFEST.in as well.
#package_data={
# 'sample': ['logging.conf'],
#},
# Although 'package_data' is the preferred approach, in some case you may
# need to place data files outside of your packages.
# see http://docs.python.org/3.4/distutils/setupscript.html#installing-additional-files
# In this case, 'data_file' will be installed into '<sys.prefix>/my_data'
#data_files=[('my_data', ['data/data_file'])],
# To provide executable scripts, use entry points in preference to the
# "scripts" keyword. Entry points provide cross-platform support and allow
# pip to create the appropriate form of executable for the target platform.
#entry_points={
# 'console_scripts': [
# 'sample=sample:main',
# ],
#},
)

@ -1,91 +0,0 @@
import constraint
from examples.abc import abc
from examples.coins import coins
# from examples.crosswords import crosswords
from examples.einstein import einstein
from examples.queens import queens
from examples.rooks import rooks
from examples.studentdesks import studentdesks
# from examples.sudoku import sudoku
# from examples.wordmath import (seisseisdoze, sendmoremoney, twotwofour)
# from examples.xsum import xsum
import constraint.compat as compat
def test_abc():
solutions = abc.solve()
minvalue, minsolution = solutions
assert minvalue == 37
assert minsolution == {'a': 1, 'c': 2, 'b': 1}
def test_coins():
solutions = coins.solve()
assert len(solutions) == 2
def test_einstein():
solutions = einstein.solve()
expected_solutions = [
{
'nationality2': 'dane',
'nationality3': 'brit',
'nationality1': 'norwegian',
'nationality4': 'german',
'nationality5': 'swede',
'color1': 'yellow',
'color3': 'red',
'color2': 'blue',
'color5': 'white',
'color4': 'green',
'drink4': 'coffee',
'drink5': 'beer',
'drink1': 'water',
'drink2': 'tea',
'drink3': 'milk',
'smoke5': 'bluemaster',
'smoke4': 'prince',
'smoke3': 'pallmall',
'smoke2': 'blends',
'smoke1': 'dunhill',
'pet5': 'dogs',
'pet4': 'fish',
'pet1': 'cats',
'pet3': 'birds',
'pet2': 'horses'
}
]
assert solutions == expected_solutions
def test_queens():
solutions, size = queens.solve()
assert size == 8
for solution in solutions:
queens.showSolution(solution, size)
def test_rooks():
size = 8
solutions = rooks.solve(size)
assert len(solutions) == rooks.factorial(size)
def test_studentdesks():
solutions = studentdesks.solve()
expected_solutions = {1: 'A', 2: 'E', 3: 'D', 4: 'E', 5: 'D', 6: 'A', 7: 'C', 8: 'B', 9: 'C', 10: 'B', 11: 'E', 12: 'D', 13: 'E', 14: 'D', 15: 'A', 16: 'C', 17: 'B', 18: 'C', 19: 'B', 20: 'A'}
assert solutions == expected_solutions
def test_constraint_without_variables():
problem = constraint.Problem()
problem.addVariable("a", [1, 2, 3])
problem.addConstraint(lambda a: a * 2 == 6)
solutions = problem.getSolutions()
assert solutions == [{'a': 3}]
def test_version():
assert isinstance(constraint.__version__, compat.string_types)

@ -1,17 +0,0 @@
from constraint import Problem, MinConflictsSolver
def test_min_conflicts_solver():
problem = Problem(MinConflictsSolver())
problem.addVariable("x", [0, 1])
problem.addVariable("y", [0, 1])
solution = problem.getSolution()
possible_solutions = [
{'x': 0, 'y': 0},
{'x': 0, 'y': 1},
{'x': 1, 'y': 0},
{'x': 1, 'y': 1}
]
assert solution in possible_solutions

@ -1,102 +0,0 @@
from constraint import Domain, Variable, SomeNotInSetConstraint
def test_empty_constraint():
constrainer = SomeNotInSetConstraint(set())
v1, v2 = variables = [Variable('v1'), Variable('v2')]
assignments = {v1: 'a', v2: 'b'}
assert constrainer(variables, {}, assignments)
def test_no_overlap():
constrainer = SomeNotInSetConstraint(set('zy'))
v1, v2 = variables = [Variable('v1'), Variable('v2')]
assignments = {v1: 'a', v2: 'b'}
assert constrainer(variables, {}, assignments)
def test_some_overlap():
constrainer = SomeNotInSetConstraint(set('b'))
v1, v2 = variables = [Variable('v1'), Variable('v2')]
assignments = {v1: 'a', v2: 'b'}
assert constrainer(variables, {}, assignments)
def test_too_much_overlap():
constrainer = SomeNotInSetConstraint(set('ab'))
v1, v2 = variables = [Variable('v1'), Variable('v2')]
assignments = {v1: 'a', v2: 'b'}
assert not constrainer(variables, {}, assignments)
def test_exact():
constrainer = SomeNotInSetConstraint(set('abc'), n=2, exact=True)
v1, v2, v3 = variables = [Variable('v1'), Variable('v2'), Variable('v3')]
assignments = {v1: 'a', v2: 'y', v3: 'z'}
assert constrainer(variables, {}, assignments)
assignments = {v1: 'a', v2: 'y'}
assert constrainer(variables, {}, assignments)
assignments = {v1: 'a', v2: 'b', v3: 'z'}
assert not constrainer(variables, {}, assignments)
assignments = {v1: 'a', v2: 'b'}
assert not constrainer(variables, {}, assignments)
assignments = {v1: 'a', v2: 'b', v3: 'c'}
assert not constrainer(variables, {}, assignments)
assignments = {v1: 'x', v2: 'y', v3: 'z'}
assert not constrainer(variables, {}, assignments)
def test_forwardcheck():
constrainer = SomeNotInSetConstraint(set('abc'), n=2)
v1, v2, v3 = variables = [Variable('v1'), Variable('v2'), Variable('v3')]
domains = {v1: Domain(['a']), v2: Domain(['b', 'y']),
v3: Domain(['c', 'z'])}
assert constrainer(variables, domains, {v1: 'a'})
assert ['a'] == list(domains[v1])
assert ['b', 'y'] == list(domains[v2])
assert ['c', 'z'] == list(domains[v3])
assert constrainer(variables, domains, {v1: 'a'}, True)
assert ['a'] == list(domains[v1])
assert ['y'] == list(domains[v2])
assert ['z'] == list(domains[v3])
def test_forwardcheck_empty_domain():
constrainer = SomeNotInSetConstraint(set('abc'))
v1, v2 = variables = [Variable('v1'), Variable('v2')]
domains = {v1: Domain(['a']), v2: Domain(['b'])}
assert constrainer(variables, domains, {v1: 'a'})
assert not constrainer(variables, domains, {v1: 'a'}, True)
def test_forwardcheck_exact():
constrainer = SomeNotInSetConstraint(set('abc'), n=2, exact=True)
v1, v2, v3 = variables = [Variable('v1'), Variable('v2'), Variable('v3')]
assignments = {v1: 'a'}
domains = {v1: Domain(['a', 'x']), v2: Domain(['b', 'y']),
v3: Domain(['c', 'z'])}
assert constrainer(variables, domains, assignments)
assert constrainer(variables, domains, assignments, True)
assert 'b' not in domains[v2]
assert 'y' in domains[v2]
assert 'c' not in domains[v3]
assert 'z' in domains[v3]
domains = {v1: Domain(['a', 'x']), v2: Domain(['b', 'y']),
v3: Domain(['c'])}
assert constrainer(variables, domains, assignments)
assert not constrainer(variables, domains, assignments, True)
Loading…
Cancel
Save