diff --git a/.travis.yml b/.travis.yml index 2bf2b8a..0ef994d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,21 +1,22 @@ language: python python: - - "2.7" - - "3.3" - - "3.4" - - "3.5" - - "3.6" - -# command to install dependencies +- '2.7' +- '3.4' +- '3.5' +- '3.6' install: - - "pip install -qq flake8" - - "pip install coveralls --quiet" - - "pip install ." - -# command to run tests +- pip install -qq flake8 +- pip install coveralls --quiet +- pip install . script: - - nosetests -s -v --with-coverage --cover-package=constraint - - flake8 --ignore E501 constraint examples tests - +- nosetests -s -v --with-coverage --cover-package=constraint +- flake8 --ignore E501,W504 constraint examples tests after_success: - - coveralls +- coveralls +deploy: + provider: pypi + user: "scls" + password: + secure: YP1I8vi04F2mhaexylEK4PoizOMKfPy6ixDGYYLg5WAvgNdHvBN39xFsd9WHavMGg0RcV3xn5jAawQN1dDnXYVNW8LAivwfkUazXjqxAf4IeMp92203kOjQij/D494etHYeIw4SNJgk7J2tDil+goITJ/OhJ4t7fDC0eA0nILn8ifyZZQUgZppW5CoAf1L8cxY1JWICXLKQFQ42zPkFaIA9oBSOgok5wlNoyguScJ70mqUwZewhZHk4L07WSFRbDEOawHe5CAHCPO8rVCkhk2WdLWRoY9uHijDxHn9eCZ3zm4ac/jAwPtFol43q5u9wTCSm8WmeVfU/mJLjgGvmyDhb5Z2fTVbWGsX/N/WHvASr85HfKS0Vq2hAHYozukLbJ8EQZL6ZoOiFhjbL1LJv6Ex3EZ3PTkjKZEGEiLS/aiLZSj95CDMnfKjaNnAN2qFxzR1yi7tFHttS7XiaTCuKoegeN/RNA1iTdFPsXIcmCklhqYr9jCoTaKOXic8W5C1ej3V8oogx1xA79/mf7ZtHxtHWeT9o7cG2EK5gYfvPi6bhKPZDQ2hq49tt8AbcjX4/ycovTmX/cTn0CCoUfLB7Ok9/UvdcdUiflVZEm4cH1WAXAeD3CW+WGTOEHSgNArl9ERxUyomsWhyhutGPmeZIPQ1COeuqFFBTIaHWDG1ytqN4= + on: + tags: true diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..bcd0769 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,39 @@ +# Change Log + +## Python Contraint + +All notable changes to this code base will be documented in this file, +in every released version. + +### Version 1.4.0 + +- Released: 2018-11-05 +- Issues/Enhancements: + - Add tests around `SomeNotInSetConstraint.` + - Minor `README` fixes + - Fix `dict.keys()` issue with Python 3 and add some unit tests + - Drop Python 3.3 support + +### Version 1.3.1 + +- Released: 2017-03-31 +- Issues/Enhancements: + - Better `README` rendering (using reStructuredText) + +### Version 1.3 + +- Released: 2017-03-31 +- Maintainer: Sébastien Celles +- Issues/Enhancements: + - Original code forked from https://labix.org/python-constraint + - Publish on Github + - Python 2 / 3 support + - Remove Python 2.6 support + - Unit tests and continuous integration + - Create a registered pip instalable package + - PEP8 + +### Initial development + +- Author: Gustavo Biemeyer +- URL: https://labix.org/python-constraint diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..f615b2d --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,46 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at s.celles@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..b8f6810 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,52 @@ +# Contributing + +If you discover issues, have ideas for improvements or new features, +please report them to the [issue tracker](https://github.com/python-constraint/python-constraint/issues) of the repository. +After this you can help by fixing it submitting a pull request (PR). +Please, try to follow these guidelines when you +do so. + +## Issue reporting + +* Check that the issue has not already been reported. +* Check that the issue has not already been fixed in the latest code + (a.k.a. `master`). So be certain that you are using latest `master` code version + (not latest released version). Installing latest development version can be done using: + +```bash +$ pip install git+https://github.com/python-constraint/python-constraint +``` + +or + +```bash +$ git clone https://github.com/python-constraint/python-constraint +$ python setup.py install +``` + +* Be clear, concise and precise in your description of the problem. +* Open an issue with a descriptive title and a summary in grammatically correct, + complete sentences. +* Mention your Python version and operating system. +* Include any relevant code to the issue summary. + +A [Minimal Working Example (MWE)](https://en.wikipedia.org/wiki/Minimal_Working_Example) can help. + +### Reporting bugs + +When reporting bugs it's a good idea to provide stacktrace messages to +the bug report makes it easier to track down bugs. Some steps to reproduce a bug +reliably would also make a huge difference. + +## Pull requests + +* Read [how to properly contribute to open source projects on Github](http://gun.io/blog/how-to-github-fork-branch-and-pull-request). +* Use a topic branch to easily amend a pull request later, if necessary. +* Use the same coding conventions as the rest of the project. +* Make sure that the unit tests are passing (`py.test`). +* Write [good commit messages](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html). +* Mention related tickets in the commit messages (e.g. `[Fix #N] Add command ...`). +* Update the [changelog](https://github.com/python-constraint/python-constraint/blob/master/CHANGELOG.md). +* [Squash related commits together](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html). +* Open a [pull request](https://help.github.com/articles/using-pull-requests) that relates to *only* one subject with a clear title + and description in grammatically correct, complete sentences. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md new file mode 100644 index 0000000..0ae7417 --- /dev/null +++ b/CONTRIBUTORS.md @@ -0,0 +1,11 @@ +# List of python-constraint contributors and notable users (sorted alphabetically) + +- Sébastien Celles - https://github.com/scls19fr - project co-maintainer +- Aron Griffis - https://github.com/agriffis +- Matt Kindy - https://github.com/mattkindy-praetorian +- Arun Mahapatra - https://github.com/codito +- Gustavo Niemeyer - https://github.com/niemeyer/ - initial author + +[Full Github contributors list](https://github.com/python-constraint/python-constraint/graphs/contributors). + +and maybe more... diff --git a/RELEASE_PROCEDURE.md b/RELEASE_PROCEDURE.md new file mode 100644 index 0000000..d8c490c --- /dev/null +++ b/RELEASE_PROCEDURE.md @@ -0,0 +1,51 @@ +# Release procedue + +* Ensure supported Python versions in `setup.py` and `.travis.yml` are corrects + +* Ensure `python-constraint` version is up to date in `version.py` + +* Ensure `CHANGELOG.md` have been updated + +* Tag commit and push to github + +using Github website + +Go to https://github.com/python-constraint/python-constraint/releases/new +tag: vx.x.x + +or using cli + +```bash +git tag -a x.x.x -m 'Version x.x.x' +git push python-constraint master --tags +``` + +* Upload to PyPI + +Ensure a `~/.pypirc` exists + +``` +[distutils] # this tells distutils what package indexes you can push to +index-servers = pypi + pypi # the live PyPI + pypitest # test PyPI + +[pypi] +repository:http://pypi.python.org/pypi +username:scls +password:********** +``` + +Upload + +``` +git clean -xfd +python setup.py register sdist bdist_wheel --universal +python setup.py sdist bdist_wheel upload +``` + +* Verify on PyPI + +Go to https://pypi.python.org/pypi/python-constraint/ + +Verify that new version is published. diff --git a/constraint/__init__.py b/constraint/__init__.py index 7932aa3..c8bec19 100644 --- a/constraint/__init__.py +++ b/constraint/__init__.py @@ -47,20 +47,40 @@ from __future__ import absolute_import, division, print_function -from .version import (__author__, __copyright__, __credits__, __license__, # noqa - __version__, __email__, __status__, __url__) # noqa +from .version import __author__ # noqa +from .version import __copyright__ # noqa +from .version import __credits__ # noqa +from .version import __license__ # noqa +from .version import __version__ # noqa +from .version import __email__ # noqa +from .version import __status__ # noqa +from .version import __url__ # noqa import random import copy from .compat import xrange -__all__ = ["Problem", "Variable", "Domain", "Unassigned", - "Solver", "BacktrackingSolver", "RecursiveBacktrackingSolver", - "MinConflictsSolver", "Constraint", "FunctionConstraint", - "AllDifferentConstraint", "AllEqualConstraint", "MaxSumConstraint", - "ExactSumConstraint", "MinSumConstraint", "InSetConstraint", - "NotInSetConstraint", "SomeInSetConstraint", - "SomeNotInSetConstraint"] +__all__ = [ + "Problem", + "Variable", + "Domain", + "Unassigned", + "Solver", + "BacktrackingSolver", + "RecursiveBacktrackingSolver", + "MinConflictsSolver", + "Constraint", + "FunctionConstraint", + "AllDifferentConstraint", + "AllEqualConstraint", + "MaxSumConstraint", + "ExactSumConstraint", + "MinSumConstraint", + "InSetConstraint", + "NotInSetConstraint", + "SomeInSetConstraint", + "SomeNotInSetConstraint", +] class Problem(object): @@ -145,7 +165,7 @@ def addVariable(self, variable, domain): if variable in self._variables: msg = "Tried to insert duplicated variable %s" % repr(variable) raise ValueError(msg) - if hasattr(domain, '__getitem__'): + if hasattr(domain, "__getitem__"): domain = Domain(domain) elif isinstance(domain, Domain): domain = copy.copy(domain) @@ -204,8 +224,7 @@ def addConstraint(self, constraint, variables=None): if callable(constraint): constraint = FunctionConstraint(constraint) else: - msg = "Constraints must be instances of subclasses "\ - "of the Constraint class" + msg = "Constraints must be instances of subclasses " "of the Constraint class" raise ValueError(msg) self._constraints.append((constraint, variables)) @@ -272,8 +291,7 @@ def getSolutionIter(self): domains, constraints, vconstraints = self._getArgs() if not domains: return iter(()) - return self._solver.getSolutionIter(domains, constraints, - vconstraints) + return self._solver.getSolutionIter(domains, constraints, vconstraints) def _getArgs(self): domains = self._variables.copy() @@ -290,8 +308,7 @@ def _getArgs(self): for variable in variables: vconstraints[variable].append((constraint, variables)) for constraint, variables in constraints[:]: - constraint.preProcess(variables, domains, - constraints, vconstraints) + constraint.preProcess(variables, domains, constraints, vconstraints) for domain in domains.values(): domain.resetState() if not domain: @@ -299,6 +316,7 @@ def _getArgs(self): # doArc8(getArcs(domains, constraints), domains, {}) return domains, constraints, vconstraints + # ---------------------------------------------------------------------- # Solvers # ---------------------------------------------------------------------- @@ -315,12 +333,8 @@ def getArcs(domains, constraints): constraint, variables = x if len(variables) == 2: variable1, variable2 = variables - arcs.setdefault(variable1, {})\ - .setdefault(variable2, [])\ - .append(x) - arcs.setdefault(variable2, {})\ - .setdefault(variable1, [])\ - .append(x) + arcs.setdefault(variable1, {}).setdefault(variable2, []).append(x) + arcs.setdefault(variable2, {}).setdefault(variable1, []).append(x) return arcs @@ -351,8 +365,9 @@ def doArc8(arcs, domains, assignments): for othervalue in otherdomain: assignments[othervariable] = othervalue for constraint, variables in arcconstraints: - if not constraint(variables, domains, - assignments, True): + if not constraint( + variables, domains, assignments, True + ): break else: # All constraints passed. Value is safe. @@ -472,8 +487,10 @@ def getSolutionIter(self, domains, constraints, vconstraints): while True: # Mix the Degree and Minimum Remaing Values (MRV) heuristics - lst = [(-len(vconstraints[variable]), - len(domains[variable]), variable) for variable in domains] + lst = [ + (-len(vconstraints[variable]), len(domains[variable]), variable) + for variable in domains + ] lst.sort() for item in lst: if item[-1] not in assignments: @@ -481,8 +498,11 @@ def getSolutionIter(self, domains, constraints, vconstraints): variable = item[-1] values = domains[variable][:] if forwardcheck: - pushdomains = [domains[x] for x in domains - if x not in assignments and x != variable] + pushdomains = [ + domains[x] + for x in domains + if x not in assignments and x != variable + ] else: pushdomains = None break @@ -521,8 +541,7 @@ def getSolutionIter(self, domains, constraints, vconstraints): domain.pushState() for constraint, variables in vconstraints[variable]: - if not constraint(variables, domains, assignments, - pushdomains): + if not constraint(variables, domains, assignments, pushdomains): # Value is not good. break else: @@ -587,12 +606,15 @@ def __init__(self, forwardcheck=True): """ self._forwardcheck = forwardcheck - def recursiveBacktracking(self, solutions, domains, vconstraints, - assignments, single): + def recursiveBacktracking( + self, solutions, domains, vconstraints, assignments, single + ): # Mix the Degree and Minimum Remaing Values (MRV) heuristics - lst = [(-len(vconstraints[variable]), - len(domains[variable]), variable) for variable in domains] + lst = [ + (-len(vconstraints[variable]), len(domains[variable]), variable) + for variable in domains + ] lst.sort() for item in lst: if item[-1] not in assignments: @@ -618,14 +640,14 @@ def recursiveBacktracking(self, solutions, domains, vconstraints, for domain in pushdomains: domain.pushState() for constraint, variables in vconstraints[variable]: - if not constraint(variables, domains, assignments, - pushdomains): + if not constraint(variables, domains, assignments, pushdomains): # Value is not good. break else: # Value is good. Recurse and get next variable. - self.recursiveBacktracking(solutions, domains, vconstraints, - assignments, single) + self.recursiveBacktracking( + solutions, domains, vconstraints, assignments, single + ) if solutions and single: return solutions if pushdomains: @@ -635,13 +657,11 @@ def recursiveBacktracking(self, solutions, domains, vconstraints, return solutions def getSolution(self, domains, constraints, vconstraints): - solutions = self.recursiveBacktracking([], domains, vconstraints, - {}, True) + solutions = self.recursiveBacktracking([], domains, vconstraints, {}, True) return solutions and solutions[0] or None def getSolutions(self, domains, constraints, vconstraints): - return self.recursiveBacktracking([], domains, vconstraints, - {}, False) + return self.recursiveBacktracking([], domains, vconstraints, {}, False) class MinConflictsSolver(Solver): @@ -719,6 +739,7 @@ def getSolution(self, domains, constraints, vconstraints): return assignments return None + # ---------------------------------------------------------------------- # Variables # ---------------------------------------------------------------------- @@ -809,6 +830,7 @@ def hideValue(self, value): list.remove(self, value) self._hidden.append(value) + # ---------------------------------------------------------------------- # Constraints # ---------------------------------------------------------------------- @@ -876,8 +898,7 @@ def preProcess(self, variables, domains, constraints, vconstraints): constraints.remove((self, variables)) vconstraints[variable].remove((self, variables)) - def forwardCheck(self, variables, domains, assignments, - _unassigned=Unassigned): + def forwardCheck(self, variables, domains, assignments, _unassigned=Unassigned): """ Helper method for generic forward checking @@ -953,14 +974,22 @@ def __init__(self, func, assigned=True): self._func = func self._assigned = assigned - def __call__(self, variables, domains, assignments, forwardcheck=False, - _unassigned=Unassigned): + def __call__( + self, + variables, + domains, + assignments, + forwardcheck=False, + _unassigned=Unassigned, + ): parms = [assignments.get(x, _unassigned) for x in variables] missing = parms.count(_unassigned) if missing: - return ((self._assigned or self._func(*parms)) and - (not forwardcheck or missing != 1 or - self.forwardCheck(variables, domains, assignments))) + return (self._assigned or self._func(*parms)) and ( + not forwardcheck or + missing != 1 or + self.forwardCheck(variables, domains, assignments) + ) return self._func(*parms) @@ -977,8 +1006,14 @@ class AllDifferentConstraint(Constraint): [[('a', 1), ('b', 2)], [('a', 2), ('b', 1)]] """ - def __call__(self, variables, domains, assignments, forwardcheck=False, - _unassigned=Unassigned): + def __call__( + self, + variables, + domains, + assignments, + forwardcheck=False, + _unassigned=Unassigned, + ): seen = {} for variable in variables: value = assignments.get(variable, _unassigned) @@ -1011,8 +1046,14 @@ class AllEqualConstraint(Constraint): [[('a', 1), ('b', 1)], [('a', 2), ('b', 2)]] """ - def __call__(self, variables, domains, assignments, forwardcheck=False, - _unassigned=Unassigned): + def __call__( + self, + variables, + domains, + assignments, + forwardcheck=False, + _unassigned=Unassigned, + ): singlevalue = _unassigned for variable in variables: value = assignments.get(variable, _unassigned) @@ -1058,8 +1099,7 @@ def __init__(self, maxsum, multipliers=None): self._multipliers = multipliers def preProcess(self, variables, domains, constraints, vconstraints): - Constraint.preProcess(self, variables, domains, - constraints, vconstraints) + Constraint.preProcess(self, variables, domains, constraints, vconstraints) multipliers = self._multipliers maxsum = self._maxsum if multipliers: @@ -1142,8 +1182,7 @@ def __init__(self, exactsum, multipliers=None): self._multipliers = multipliers def preProcess(self, variables, domains, constraints, vconstraints): - Constraint.preProcess(self, variables, domains, - constraints, vconstraints) + Constraint.preProcess(self, variables, domains, constraints, vconstraints) multipliers = self._multipliers exactsum = self._exactsum if multipliers: @@ -1458,4 +1497,5 @@ def __call__(self, variables, domains, assignments, forwardcheck=False): if __name__ == "__main__": import doctest + doctest.testmod() diff --git a/constraint/compat.py b/constraint/compat.py index ef31a00..c20c223 100644 --- a/constraint/compat.py +++ b/constraint/compat.py @@ -4,7 +4,7 @@ import sys PY2 = sys.version_info[0] == 2 -PY3 = (sys.version_info[0] >= 3) +PY3 = sys.version_info[0] >= 3 if PY3: string_types = str diff --git a/constraint/version.py b/constraint/version.py index af97862..b3dddc1 100644 --- a/constraint/version.py +++ b/constraint/version.py @@ -1,8 +1,8 @@ __author__ = "Gustavo Niemeyer" -__copyright__ = "Copyright (c) 2005-2014 - Gustavo Niemeyer " +__copyright__ = "Copyright (c) 2005-2018 - Gustavo Niemeyer " __credits__ = ["Sebastien Celles"] __license__ = "" -__version__ = "1.3.1" +__version__ = "1.4.0" __email__ = "gustavo@niemeyer.net" __status__ = "Development" -__url__ = 'https://github.com/python-constraint/python-constraint' +__url__ = "https://github.com/python-constraint/python-constraint" diff --git a/examples/crosswords/crosswords.py b/examples/crosswords/crosswords.py index df0fce6..dc9b79d 100755 --- a/examples/crosswords/crosswords.py +++ b/examples/crosswords/crosswords.py @@ -72,21 +72,24 @@ def main(puzzle, lines): 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)) + 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,)) + 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,)) + problem.addConstraint( + lambda vw, vci=vci, letter=letter: vw[vci] == letter, ("v%d" % vi,) + ) wordsbylen = {} for hword in horizontal: diff --git a/examples/einstein/einstein.py b/examples/einstein/einstein.py index 2ce6e45..894ee90 100755 --- a/examples/einstein/einstein.py +++ b/examples/einstein/einstein.py @@ -43,143 +43,155 @@ 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)]) + 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)) + 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)) + 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)) + 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))) + 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,)) + 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)) + 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)) + 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)) + 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,)) + problem.addConstraint(lambda drink: drink == "milk", ("drink%d" % i,)) # Hint 9 if i == 1: - problem.addConstraint(lambda nationality: - nationality == "norwegian", - ("nationality%d" % i,)) + 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))) + 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))) + 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))) + 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))) + 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)) + 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)) + 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))) + 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))) + 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))) + 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))) + 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 diff --git a/examples/queens/queens.py b/examples/queens/queens.py index 88aa565..0d3dbc5 100755 --- a/examples/queens/queens.py +++ b/examples/queens/queens.py @@ -15,9 +15,12 @@ def solve(): 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)) + 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 diff --git a/examples/rooks/rooks.py b/examples/rooks/rooks.py index a797901..7422e44 100755 --- a/examples/rooks/rooks.py +++ b/examples/rooks/rooks.py @@ -18,8 +18,7 @@ def solve(size): for col1 in cols: for col2 in cols: if col1 < col2: - problem.addConstraint(lambda row1, row2: row1 != row2, - (col1, col2)) + problem.addConstraint(lambda row1, row2: row1 != row2, (col1, col2)) solutions = problem.getSolutions() return solutions diff --git a/examples/studentdesks/studentdesks.py b/examples/studentdesks/studentdesks.py index a2978ec..0d9e294 100755 --- a/examples/studentdesks/studentdesks.py +++ b/examples/studentdesks/studentdesks.py @@ -5,12 +5,14 @@ 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]] +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(): @@ -23,8 +25,12 @@ def solve(): 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 = [ + 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() diff --git a/examples/sudoku/sudoku.py b/examples/sudoku/sudoku.py index 820c76d..6be0a6f 100644 --- a/examples/sudoku/sudoku.py +++ b/examples/sudoku/sudoku.py @@ -21,34 +21,55 @@ def solve(): 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(), [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(), [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]) + 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]] + 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,)) + problem.addConstraint( + lambda var, val=initValue[i - 1][j - 1]: var == val, (i * 10 + j,) + ) # Get the solutions. solutions = problem.getSolutions() diff --git a/examples/wordmath/seisseisdoze.py b/examples/wordmath/seisseisdoze.py index 22776db..4e37a2b 100755 --- a/examples/wordmath/seisseisdoze.py +++ b/examples/wordmath/seisseisdoze.py @@ -15,11 +15,14 @@ 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 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()) @@ -31,8 +34,7 @@ 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 + 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__": diff --git a/examples/wordmath/sendmoremoney.py b/examples/wordmath/sendmoremoney.py index 9e9578e..6fd95a7 100755 --- a/examples/wordmath/sendmoremoney.py +++ b/examples/wordmath/sendmoremoney.py @@ -15,14 +15,26 @@ 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( + 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() @@ -33,9 +45,11 @@ 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) + 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__": diff --git a/examples/wordmath/twotwofour.py b/examples/wordmath/twotwofour.py index 33e4aab..c1624d9 100755 --- a/examples/wordmath/twotwofour.py +++ b/examples/wordmath/twotwofour.py @@ -15,11 +15,14 @@ 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( + 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() diff --git a/examples/xsum/xsum.py b/examples/xsum/xsum.py index 987438f..d6d055d 100755 --- a/examples/xsum/xsum.py +++ b/examples/xsum/xsum.py @@ -15,10 +15,12 @@ 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( + 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 @@ -32,16 +34,25 @@ def main(): def showSolutions(solutions): for solution in solutions: - print(""" %d %d + 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"])) +""" + % ( + solution["a"], + solution["e"], + solution["b"], + solution["f"], + solution["x"], + solution["g"], + solution["c"], + solution["h"], + solution["d"], + ) + ) if __name__ == "__main__": diff --git a/setup.py b/setup.py index 4a597e5..77b0253 100755 --- a/setup.py +++ b/setup.py @@ -6,118 +6,97 @@ from os import path import io -NAME = 'python-constraint' -filename = "%s/version.py" % 'constraint' +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: + 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='0.0.1', version=__version__, - - description="python-constraint is a module implementing support "\ - "for handling CSPs (Constraint Solving Problems) over finite domain", - + 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', - + "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', - + "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', - + "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', - + "License :: OSI Approved :: BSD License", ], - # What does your project relate to? - keywords='csp constraint solving problems problem solver', - + 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*']), - + 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'], - }, - + 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={ + # 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 '/my_data' - #data_files=[('my_data', ['data/data_file'])], - + # 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={ + # entry_points={ # 'console_scripts': [ # 'sample=sample:main', # ], - #}, + # }, ) diff --git a/tests/test_constraint.py b/tests/test_constraint.py index e644bf4..d4ef738 100644 --- a/tests/test_constraint.py +++ b/tests/test_constraint.py @@ -2,11 +2,13 @@ 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 @@ -18,7 +20,7 @@ def test_abc(): solutions = abc.solve() minvalue, minsolution = solutions assert minvalue == 37 - assert minsolution == {'a': 1, 'c': 2, 'b': 1} + assert minsolution == {"a": 1, "c": 2, "b": 1} def test_coins(): @@ -30,31 +32,31 @@ 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' + "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 @@ -75,7 +77,28 @@ def test_rooks(): 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'} + 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 @@ -84,7 +107,7 @@ def test_constraint_without_variables(): problem.addVariable("a", [1, 2, 3]) problem.addConstraint(lambda a: a * 2 == 6) solutions = problem.getSolutions() - assert solutions == [{'a': 3}] + assert solutions == [{"a": 3}] def test_version(): diff --git a/tests/test_solvers.py b/tests/test_solvers.py index 1a24d38..3fac3c9 100644 --- a/tests/test_solvers.py +++ b/tests/test_solvers.py @@ -8,10 +8,10 @@ def test_min_conflicts_solver(): solution = problem.getSolution() possible_solutions = [ - {'x': 0, 'y': 0}, - {'x': 0, 'y': 1}, - {'x': 1, 'y': 0}, - {'x': 1, 'y': 1} + {"x": 0, "y": 0}, + {"x": 0, "y": 1}, + {"x": 1, "y": 0}, + {"x": 1, "y": 1}, ] assert solution in possible_solutions diff --git a/tests/test_some_not_in_set.py b/tests/test_some_not_in_set.py index 31ac4fc..c625285 100644 --- a/tests/test_some_not_in_set.py +++ b/tests/test_some_not_in_set.py @@ -3,100 +3,97 @@ def test_empty_constraint(): constrainer = SomeNotInSetConstraint(set()) - v1, v2 = variables = [Variable('v1'), Variable('v2')] - assignments = {v1: 'a', v2: 'b'} + 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'} + 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'} + 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'} + 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')] + 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'} + assignments = {v1: "a", v2: "y", v3: "z"} assert constrainer(variables, {}, assignments) - assignments = {v1: 'a', v2: 'y'} + assignments = {v1: "a", v2: "y"} assert constrainer(variables, {}, assignments) - assignments = {v1: 'a', v2: 'b', v3: 'z'} + assignments = {v1: "a", v2: "b", v3: "z"} assert not constrainer(variables, {}, assignments) - assignments = {v1: 'a', v2: 'b'} + assignments = {v1: "a", v2: "b"} assert not constrainer(variables, {}, assignments) - assignments = {v1: 'a', v2: 'b', v3: 'c'} + assignments = {v1: "a", v2: "b", v3: "c"} assert not constrainer(variables, {}, assignments) - assignments = {v1: 'x', v2: 'y', v3: 'z'} + 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')] + 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]) + 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]) + 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')] + 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) + 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'} + 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'])} + 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] + 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'])} + domains = {v1: Domain(["a", "x"]), v2: Domain(["b", "y"]), v3: Domain(["c"])} assert constrainer(variables, domains, assignments) assert not constrainer(variables, domains, assignments, True)