diff --git a/examples/split_problems.md b/examples/split_problems.md new file mode 100644 index 0000000..d4a4a46 --- /dev/null +++ b/examples/split_problems.md @@ -0,0 +1,86 @@ +To make computation more tractable, a tiling problem can be split into two or more sub-problems. Each solution to the original problem is also the solution to exactly one of the sub-problems. + +``` +>>> from polyomino.board import Chessboard +>>> from polyomino.constant import TETROMINOS, ALL_PENTOMINOS + +``` + +First a problem which involves no repeated tiles. It can be solved without splitting, but let us look at how splitting works: +``` +>>> problem = Chessboard().tile_with(ALL_PENTOMINOS + [TETROMINOS['Square']]) +>>> subproblems = problem.split(at_least=2) +>>> len(subproblems) +3 +>>> example = subproblems[0] +>>> example + +>>> print(example.display()) ++-+-+-+-+-+-+-+-+ +| | | | ++ +-+-+-+-+-+ + +| | | | ++-+ +-+ +-+-+-+-+ +| | | | | | ++ +-+-+-+ +-+-+ + +| | | | ++-+-+-+-+-+-+ + + +| | | | | ++-+ +-+-+ +-+-+-+ +| | | | | | | ++ +-+ + +-+ +-+ + +| | | | | | ++ +-+ +-+ +-+ + +| | | | | ++-+-+-+-+-+-+-+-+ +>>> sub_solution = example.solve() +>>> sub_solution + +>>> example.is_solution(sub_solution.array) +True +>>> sub_solution.tiling +[[(0, 0), (1, 1), (1, 0), (2, 0)], [(0, 3), (1, 2), (0, 2), (0, 1)], [(3, 3), (2, 2), (2, 3), (1, 3)], [(3, 0), (2, 1), (3, 1), (3, 2)]] +>>> sub_solution.display() ++-+-+-+-+-+-+-+-+ +| | | | ++ +-+-+-+-+-+ + +| | | | ++-+ +-+ +-+-+-+-+ +| | | | | | ++ +-+-+-+ +-+-+ + +| | | | ++-+-+-+-+-+-+ + + +| | | | | ++-+ +-+-+ +-+-+-+ +| | | | | | | ++ +-+ + +-+ +-+ + +| | | | | | ++ +-+ +-+ +-+ + +| | | | | ++-+-+-+-+-+-+-+-+ +>>> solution = example.original_solution(sub_solution) +>>> solution + +>>> problem.is_solution(solution.array) +True +>>> solution.tiling +[[(0, 0), (1, 1), (1, 0), (2, 0)], [(0, 3), (1, 2), (0, 2), (0, 1)], [(3, 3), (2, 2), (2, 3), (1, 3)], [(3, 0), (2, 1), (3, 1), (3, 2)]] +>>> solution.display() ++-+-+-+-+-+-+-+-+ +| | | | ++ +-+-+-+-+-+ + +| | | | ++-+ +-+ +-+-+-+-+ +| | | | | | ++ +-+-+-+ +-+-+ + +| | | | ++-+-+-+-+-+-+ + + +| | | | | ++-+ +-+-+ +-+-+-+ +| | | | | | | ++ +-+ + +-+ +-+ + +| | | | | | ++ +-+ +-+ +-+ + +| | | | | ++-+-+-+-+-+-+-+-+ +``` diff --git a/polyomino/problem.py b/polyomino/problem.py index 9c40c4a..fb32c97 100644 --- a/polyomino/problem.py +++ b/polyomino/problem.py @@ -1,6 +1,7 @@ import numpy as np from exact_cover import get_exact_cover +from exact_cover.helpers import split_problem from .error import CantPlaceSinglePiece from .solution import Solution @@ -76,6 +77,14 @@ def output_array(self, filename): def set_name(self, name): self._name = name + def split(self, at_least=1): + arrays = split_problem(self.array, at_least) + for array in arrays: + problem = TilingProblem(self.board, self.tileset) + problem.array = array + problem.key = self.key + yield problem + @property def size(self): self.make_problem() diff --git a/pyproject.toml b/pyproject.toml index 8d6ad3a..c7889cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ license = "MIT" [tool.poetry.dependencies] python = "^3.7.1" -exact-cover = "0.4.3" +exact-cover = "0.8.0" numpy = "^1.20" pretty-poly = "0.2.0"