multi-puzzle-solver 1.0.8__tar.gz → 1.0.9__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of multi-puzzle-solver might be problematic. Click here for more details.
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/PKG-INFO +13 -9
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/README.md +12 -8
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/PKG-INFO +13 -9
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/__init__.py +1 -1
- multi_puzzle_solver-1.0.9/src/puzzle_solver/puzzles/binairo/binairo.py +98 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_binairo.py +25 -0
- multi_puzzle_solver-1.0.8/src/puzzle_solver/puzzles/binairo/binairo.py +0 -126
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/pyproject.toml +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/setup.cfg +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/SOURCES.txt +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/dependency_links.txt +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/requires.txt +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/top_level.txt +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/core/utils.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/core/utils_ortools.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/core/utils_visualizer.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/abc_view/abc_view.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/aquarium/aquarium.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/battleships/battleships.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/binairo/binairo_plus.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/black_box/black_box.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/bridges/bridges.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/chess_range/chess_melee.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/chess_range/chess_range.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/chess_range/chess_solo.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/connect_the_dots/connect_the_dots.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/dominosa/dominosa.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/filling/filling.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/flip/flip.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/flood_it/flood_it.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/flood_it/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/galaxies/galaxies.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/galaxies/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/guess/guess.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/heyawake/heyawake.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/inertia/inertia.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/inertia/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/inertia/tsp.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/kakurasu/kakurasu.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/kakuro/kakuro.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/keen/keen.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/light_up/light_up.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/lits/lits.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/magnets/magnets.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/map/map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/mathema_grids/mathema_grids.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/minesweeper/minesweeper.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/mosaic/mosaic.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/nonograms/nonograms.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/nonograms/nonograms_colored.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/norinori/norinori.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/nurikabe/nurikabe.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/palisade/palisade.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/pearl/pearl.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/pipes/pipes.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/range/range.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/rectangles/rectangles.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/shakashaka/shakashaka.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/shingoki/shingoki.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/signpost/signpost.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/singles/singles.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/slant/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/slant/slant.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/slitherlink/slitherlink.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/star_battle/star_battle.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/star_battle/star_battle_shapeless.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/stitches/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/stitches/stitches.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/sudoku/sudoku.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tapa/tapa.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tents/tents.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/thermometers/thermometers.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/towers/towers.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tracks/tracks.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/twiddle/twiddle.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/undead/undead.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/unequal/unequal.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/unruly/unruly.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/yin_yang/parse_map/parse_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/yin_yang/yin_yang.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/utils/visualizer.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_abc_view.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_aquarium.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_battleships.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_binairo_plus.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_black_box.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_bridges.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_chess_melee.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_chess_range.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_chess_solo.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_connect_the_dots.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_dominosa.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_filling.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_flip.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_flood_it.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_galaxies.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_guess.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_heyawake.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_inertia.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_kakurasu.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_kakuro.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_keen.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_light_up.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_lits.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_magnets.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_map.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_mathema_grids.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_minesweeper.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_mosaic.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_nonograms.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_nonograms_colored.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_norinori.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_nurikabe.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_palisade.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_pearl.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_pipes.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_range.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_rectangles.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_shakashaka.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_shingoki.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_signpost.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_singles.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_slant.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_slitherlink.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_star_battle.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_stitches.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_sudoku.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_tapa.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_tents.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_thermometers.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_towers.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_tracks.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_twiddle.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_undead.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_unequal.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_unruly.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_utils.py +0 -0
- {multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/tests/test_yin_yang.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-puzzle-solver
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Efficient solvers for countless (50+) types of puzzles (like Sudoku, Minesweeper, etc.) with a simple python API.
|
|
5
5
|
Author: Ar-Kareem
|
|
6
6
|
Project-URL: Homepage, https://github.com/Ar-Kareem/puzzle_solver
|
|
@@ -610,7 +610,15 @@ Time taken: 0.04 seconds
|
|
|
610
610
|
|
|
611
611
|
## Sudoku (Puzzle Type #2)
|
|
612
612
|
|
|
613
|
-
Also known as Number Place and Solo.
|
|
613
|
+
Also known as Number Place and Solo.
|
|
614
|
+
|
|
615
|
+
The code can:
|
|
616
|
+
|
|
617
|
+
1. Solve arbitrarily sized valid board sizes, thus can be used to solve:
|
|
618
|
+
- Hex Sudoku (a 16x16 variant)
|
|
619
|
+
- Kidoku (a kid-friendly sudoku variant)
|
|
620
|
+
2. Solve the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) using the optional parameter `sandwich={'side': [...], 'bottom': [...]}`
|
|
621
|
+
3. Solve the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) using the optional parameter `unique_diagonal=True`
|
|
614
622
|
|
|
615
623
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/solo.html)
|
|
616
624
|
|
|
@@ -635,12 +643,6 @@ You are given some of the numbers as clues; your aim is to place the rest of the
|
|
|
635
643
|
|
|
636
644
|
Code to utilize this package and solve the puzzle:
|
|
637
645
|
|
|
638
|
-
Note:
|
|
639
|
-
|
|
640
|
-
- The solver also supports solving the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) through the optional parameter ``sandwich={'side': [...], 'bottom': [...] }``。
|
|
641
|
-
|
|
642
|
-
- The solver also supports solving the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) through the optional parameter ``unique_diagonal=True``。
|
|
643
|
-
|
|
644
646
|
```python
|
|
645
647
|
import numpy as np
|
|
646
648
|
from puzzle_solver import sudoku_solver as solver
|
|
@@ -1105,7 +1107,7 @@ Time taken: 0.15 seconds
|
|
|
1105
1107
|
|
|
1106
1108
|
## Keen (Puzzle Type #8)
|
|
1107
1109
|
|
|
1108
|
-
Also known as KenKen or
|
|
1110
|
+
Also known as KenKen, CalcuDoku, Inkies, or Inky.
|
|
1109
1111
|
|
|
1110
1112
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/keen.html)
|
|
1111
1113
|
|
|
@@ -4043,6 +4045,8 @@ Applying the solution to the puzzle visually:
|
|
|
4043
4045
|
|
|
4044
4046
|
## Binairo (Puzzle Type #41)
|
|
4045
4047
|
|
|
4048
|
+
Also known as Takuzu, Binero, Tohu-Wa-Vohu (Formless and Empty), Eins und Zwei (One and Two), Binary Puzzles, Binoxxo, Binox, Zernero, Tic-Tac-Logic, and Sudoku Binary.
|
|
4049
|
+
|
|
4046
4050
|
* [**Play online**](https://www.puzzle-binairo.com)
|
|
4047
4051
|
|
|
4048
4052
|
* [**Solver Code**][41]
|
|
@@ -584,7 +584,15 @@ Time taken: 0.04 seconds
|
|
|
584
584
|
|
|
585
585
|
## Sudoku (Puzzle Type #2)
|
|
586
586
|
|
|
587
|
-
Also known as Number Place and Solo.
|
|
587
|
+
Also known as Number Place and Solo.
|
|
588
|
+
|
|
589
|
+
The code can:
|
|
590
|
+
|
|
591
|
+
1. Solve arbitrarily sized valid board sizes, thus can be used to solve:
|
|
592
|
+
- Hex Sudoku (a 16x16 variant)
|
|
593
|
+
- Kidoku (a kid-friendly sudoku variant)
|
|
594
|
+
2. Solve the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) using the optional parameter `sandwich={'side': [...], 'bottom': [...]}`
|
|
595
|
+
3. Solve the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) using the optional parameter `unique_diagonal=True`
|
|
588
596
|
|
|
589
597
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/solo.html)
|
|
590
598
|
|
|
@@ -609,12 +617,6 @@ You are given some of the numbers as clues; your aim is to place the rest of the
|
|
|
609
617
|
|
|
610
618
|
Code to utilize this package and solve the puzzle:
|
|
611
619
|
|
|
612
|
-
Note:
|
|
613
|
-
|
|
614
|
-
- The solver also supports solving the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) through the optional parameter ``sandwich={'side': [...], 'bottom': [...] }``。
|
|
615
|
-
|
|
616
|
-
- The solver also supports solving the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) through the optional parameter ``unique_diagonal=True``。
|
|
617
|
-
|
|
618
620
|
```python
|
|
619
621
|
import numpy as np
|
|
620
622
|
from puzzle_solver import sudoku_solver as solver
|
|
@@ -1079,7 +1081,7 @@ Time taken: 0.15 seconds
|
|
|
1079
1081
|
|
|
1080
1082
|
## Keen (Puzzle Type #8)
|
|
1081
1083
|
|
|
1082
|
-
Also known as KenKen or
|
|
1084
|
+
Also known as KenKen, CalcuDoku, Inkies, or Inky.
|
|
1083
1085
|
|
|
1084
1086
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/keen.html)
|
|
1085
1087
|
|
|
@@ -4017,6 +4019,8 @@ Applying the solution to the puzzle visually:
|
|
|
4017
4019
|
|
|
4018
4020
|
## Binairo (Puzzle Type #41)
|
|
4019
4021
|
|
|
4022
|
+
Also known as Takuzu, Binero, Tohu-Wa-Vohu (Formless and Empty), Eins und Zwei (One and Two), Binary Puzzles, Binoxxo, Binox, Zernero, Tic-Tac-Logic, and Sudoku Binary.
|
|
4023
|
+
|
|
4020
4024
|
* [**Play online**](https://www.puzzle-binairo.com)
|
|
4021
4025
|
|
|
4022
4026
|
* [**Solver Code**][41]
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-puzzle-solver
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.9
|
|
4
4
|
Summary: Efficient solvers for countless (50+) types of puzzles (like Sudoku, Minesweeper, etc.) with a simple python API.
|
|
5
5
|
Author: Ar-Kareem
|
|
6
6
|
Project-URL: Homepage, https://github.com/Ar-Kareem/puzzle_solver
|
|
@@ -610,7 +610,15 @@ Time taken: 0.04 seconds
|
|
|
610
610
|
|
|
611
611
|
## Sudoku (Puzzle Type #2)
|
|
612
612
|
|
|
613
|
-
Also known as Number Place and Solo.
|
|
613
|
+
Also known as Number Place and Solo.
|
|
614
|
+
|
|
615
|
+
The code can:
|
|
616
|
+
|
|
617
|
+
1. Solve arbitrarily sized valid board sizes, thus can be used to solve:
|
|
618
|
+
- Hex Sudoku (a 16x16 variant)
|
|
619
|
+
- Kidoku (a kid-friendly sudoku variant)
|
|
620
|
+
2. Solve the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) using the optional parameter `sandwich={'side': [...], 'bottom': [...]}`
|
|
621
|
+
3. Solve the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) using the optional parameter `unique_diagonal=True`
|
|
614
622
|
|
|
615
623
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/solo.html)
|
|
616
624
|
|
|
@@ -635,12 +643,6 @@ You are given some of the numbers as clues; your aim is to place the rest of the
|
|
|
635
643
|
|
|
636
644
|
Code to utilize this package and solve the puzzle:
|
|
637
645
|
|
|
638
|
-
Note:
|
|
639
|
-
|
|
640
|
-
- The solver also supports solving the ["Sandwich" sudoku variant](https://dkmgames.com/SandwichSudoku/) through the optional parameter ``sandwich={'side': [...], 'bottom': [...] }``。
|
|
641
|
-
|
|
642
|
-
- The solver also supports solving the ["Sudoku-X" variant](https://www.sudopedia.org/wiki/Sudoku-X) through the optional parameter ``unique_diagonal=True``。
|
|
643
|
-
|
|
644
646
|
```python
|
|
645
647
|
import numpy as np
|
|
646
648
|
from puzzle_solver import sudoku_solver as solver
|
|
@@ -1105,7 +1107,7 @@ Time taken: 0.15 seconds
|
|
|
1105
1107
|
|
|
1106
1108
|
## Keen (Puzzle Type #8)
|
|
1107
1109
|
|
|
1108
|
-
Also known as KenKen or
|
|
1110
|
+
Also known as KenKen, CalcuDoku, Inkies, or Inky.
|
|
1109
1111
|
|
|
1110
1112
|
* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/keen.html)
|
|
1111
1113
|
|
|
@@ -4043,6 +4045,8 @@ Applying the solution to the puzzle visually:
|
|
|
4043
4045
|
|
|
4044
4046
|
## Binairo (Puzzle Type #41)
|
|
4045
4047
|
|
|
4048
|
+
Also known as Takuzu, Binero, Tohu-Wa-Vohu (Formless and Empty), Eins und Zwei (One and Two), Binary Puzzles, Binoxxo, Binox, Zernero, Tic-Tac-Logic, and Sudoku Binary.
|
|
4049
|
+
|
|
4046
4050
|
* [**Play online**](https://www.puzzle-binairo.com)
|
|
4047
4051
|
|
|
4048
4052
|
* [**Solver Code**][41]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ortools.sat.python import cp_model
|
|
5
|
+
from ortools.sat.python.cp_model import LinearExpr as lxp
|
|
6
|
+
|
|
7
|
+
from puzzle_solver.core.utils import Direction, Pos, get_all_pos, get_next_pos, get_pos, in_bounds, get_char, get_row_pos, get_col_pos
|
|
8
|
+
from puzzle_solver.core.utils_ortools import generic_solve_all, SingleSolution
|
|
9
|
+
from puzzle_solver.core.utils_visualizer import combined_function
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Board:
|
|
13
|
+
def __init__(self, board: np.array, arith_rows: Optional[np.array] = None, arith_cols: Optional[np.array] = None, force_unique: bool = True, disallow_three: bool = True):
|
|
14
|
+
assert board.ndim == 2, f'board must be 2d, got {board.ndim}'
|
|
15
|
+
assert board.shape[0] % 2 == 0 and board.shape[1] % 2 == 0, f'board must have even number of rows and columns, got {board.shape[0]}x{board.shape[1]}'
|
|
16
|
+
assert all(c.item() in [' ', 'B', 'W'] for c in np.nditer(board)), 'board must contain only space or B'
|
|
17
|
+
assert arith_rows is None or all(isinstance(c.item(), str) and c.item() in [' ', 'x', '='] for c in np.nditer(arith_rows)), 'arith_rows must contain only space, x, or ='
|
|
18
|
+
assert arith_cols is None or all(isinstance(c.item(), str) and c.item() in [' ', 'x', '='] for c in np.nditer(arith_cols)), 'arith_cols must contain only space, x, or ='
|
|
19
|
+
self.board = board
|
|
20
|
+
self.V, self.H = board.shape
|
|
21
|
+
self.arith_rows = arith_rows
|
|
22
|
+
self.arith_cols = arith_cols
|
|
23
|
+
self.force_unique = force_unique
|
|
24
|
+
self.disallow_three = disallow_three
|
|
25
|
+
|
|
26
|
+
self.model = cp_model.CpModel()
|
|
27
|
+
self.model_vars: dict[Pos, cp_model.IntVar] = {}
|
|
28
|
+
self.create_vars()
|
|
29
|
+
self.add_all_constraints()
|
|
30
|
+
|
|
31
|
+
def create_vars(self):
|
|
32
|
+
for pos in get_all_pos(self.V, self.H):
|
|
33
|
+
self.model_vars[pos] = self.model.NewBoolVar(f'{pos}')
|
|
34
|
+
|
|
35
|
+
def add_all_constraints(self):
|
|
36
|
+
for pos in get_all_pos(self.V, self.H): # force clues
|
|
37
|
+
c = get_char(self.board, pos).strip()
|
|
38
|
+
if c:
|
|
39
|
+
self.model.Add(self.model_vars[pos] == (c == 'B'))
|
|
40
|
+
# 1. Each row and each column must contain an equal number of white and black circles.
|
|
41
|
+
for row in range(self.V):
|
|
42
|
+
row_vars = [self.model_vars[pos] for pos in get_row_pos(row, self.H)]
|
|
43
|
+
self.model.Add(lxp.sum(row_vars) == len(row_vars) // 2)
|
|
44
|
+
for col in range(self.H):
|
|
45
|
+
col_vars = [self.model_vars[pos] for pos in get_col_pos(col, self.V)]
|
|
46
|
+
self.model.Add(lxp.sum(col_vars) == len(col_vars) // 2)
|
|
47
|
+
# 2. No three consecutive cells of the same color
|
|
48
|
+
if self.disallow_three:
|
|
49
|
+
for pos in get_all_pos(self.V, self.H):
|
|
50
|
+
self.disallow_three_in_a_row(pos, Direction.RIGHT)
|
|
51
|
+
self.disallow_three_in_a_row(pos, Direction.DOWN)
|
|
52
|
+
# 3. Each row and column is unique.
|
|
53
|
+
if self.force_unique:
|
|
54
|
+
self.force_unique_double_list([[self.model_vars[pos] for pos in get_row_pos(row, self.H)] for row in range(self.V)])
|
|
55
|
+
self.force_unique_double_list([[self.model_vars[pos] for pos in get_col_pos(col, self.V)] for col in range(self.H)])
|
|
56
|
+
# if arithmetic is provided, add constraints for it
|
|
57
|
+
if self.arith_rows is not None:
|
|
58
|
+
self.force_arithmetic(self.arith_rows, Direction.RIGHT, self.V, self.H-1)
|
|
59
|
+
if self.arith_cols is not None:
|
|
60
|
+
self.force_arithmetic(self.arith_cols, Direction.DOWN, self.V-1, self.H)
|
|
61
|
+
|
|
62
|
+
def disallow_three_in_a_row(self, p1: Pos, direction: Direction):
|
|
63
|
+
p2 = get_next_pos(p1, direction)
|
|
64
|
+
p3 = get_next_pos(p2, direction)
|
|
65
|
+
if all(in_bounds(p, self.V, self.H) for p in [p1, p2, p3]):
|
|
66
|
+
self.model.AddBoolOr([self.model_vars[p1], self.model_vars[p2], self.model_vars[p3]])
|
|
67
|
+
self.model.AddBoolOr([self.model_vars[p1].Not(), self.model_vars[p2].Not(), self.model_vars[p3].Not()])
|
|
68
|
+
|
|
69
|
+
def force_unique_double_list(self, model_vars: list[list[cp_model.IntVar]]):
|
|
70
|
+
m = len(model_vars[0])
|
|
71
|
+
assert m <= 61, f'Too many cells for binary encoding in int64: m={m}, model_vars={model_vars}'
|
|
72
|
+
codes = []
|
|
73
|
+
pow2 = [2**k for k in range(m)]
|
|
74
|
+
for i, line in enumerate(model_vars):
|
|
75
|
+
code = self.model.NewIntVar(0, 2**m, f"code_{i}")
|
|
76
|
+
self.model.Add(code == lxp.weighted_sum(line, pow2)) # Sum 2^k * r[k] == code
|
|
77
|
+
codes.append(code)
|
|
78
|
+
self.model.AddAllDifferent(codes)
|
|
79
|
+
|
|
80
|
+
def force_arithmetic(self, arith_board: np.array, direction: Direction, V: int, H: int):
|
|
81
|
+
assert arith_board.shape == (V, H), f'arith_board going {direction} expected shape {V}x{H}, got {arith_board.shape}'
|
|
82
|
+
for pos in get_all_pos(V, H):
|
|
83
|
+
c = get_char(arith_board, pos).strip()
|
|
84
|
+
if c == 'x':
|
|
85
|
+
self.model.Add(self.model_vars[pos] != self.model_vars[get_next_pos(pos, direction)])
|
|
86
|
+
elif c == '=':
|
|
87
|
+
self.model.Add(self.model_vars[pos] == self.model_vars[get_next_pos(pos, direction)])
|
|
88
|
+
|
|
89
|
+
def solve_and_print(self, verbose: bool = True):
|
|
90
|
+
def board_to_solution(board: Board, solver: cp_model.CpSolverSolutionCallback) -> SingleSolution:
|
|
91
|
+
assignment: dict[Pos, int] = {}
|
|
92
|
+
for pos, var in board.model_vars.items():
|
|
93
|
+
assignment[pos] = solver.Value(var)
|
|
94
|
+
return SingleSolution(assignment=assignment)
|
|
95
|
+
def callback(single_res: SingleSolution):
|
|
96
|
+
print("Solution found")
|
|
97
|
+
print(combined_function(self.V, self.H, is_shaded=lambda r, c: single_res.assignment[get_pos(x=c, y=r)] == 1))
|
|
98
|
+
return generic_solve_all(self, board_to_solution, callback=callback if verbose else None, verbose=verbose)
|
|
@@ -4,6 +4,30 @@ from puzzle_solver import binairo_solver as solver
|
|
|
4
4
|
from puzzle_solver.core.utils import get_pos
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
def test_toy():
|
|
8
|
+
# tests that each row is actually unique
|
|
9
|
+
board = np.array([
|
|
10
|
+
['B', ' ', 'B', ' '],
|
|
11
|
+
[' ', ' ', 'B', ' '],
|
|
12
|
+
['W', ' ', ' ', ' '],
|
|
13
|
+
[' ', 'B', ' ', ' '],
|
|
14
|
+
])
|
|
15
|
+
binst = solver.Board(board=board)
|
|
16
|
+
solutions = binst.solve_and_print()
|
|
17
|
+
assert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'
|
|
18
|
+
solution = solutions[0].assignment
|
|
19
|
+
ground = np.array([
|
|
20
|
+
['B', 'W', 'B', 'W'],
|
|
21
|
+
['W', 'W', 'B', 'B'],
|
|
22
|
+
['W', 'B', 'W', 'B'],
|
|
23
|
+
['B', 'B', 'W', 'W'],
|
|
24
|
+
])
|
|
25
|
+
ground_assignment = {get_pos(x=x, y=y): 1 if ground[y][x] == 'B' else 0 for x in range(ground.shape[1]) for y in range(ground.shape[0])}
|
|
26
|
+
assert set(solution.keys()) == set(ground_assignment.keys()), f'solution keys != ground assignment keys, {set(solution.keys()) ^ set(ground_assignment.keys())} \n\n\n{solution} \n\n\n{ground_assignment}'
|
|
27
|
+
for pos in solution.keys():
|
|
28
|
+
assert solution[pos] == ground_assignment[pos], f'solution[{pos}] != ground_assignment[{pos}], {solution[pos]} != {ground_assignment[pos]}'
|
|
29
|
+
|
|
30
|
+
|
|
7
31
|
def test_easy():
|
|
8
32
|
# 6x6 easy
|
|
9
33
|
# https://www.puzzle-binairo.com/binairo-6x6-easy/?e=MDoxLDkxMSwwMzg=
|
|
@@ -121,6 +145,7 @@ def test_ground():
|
|
|
121
145
|
|
|
122
146
|
|
|
123
147
|
if __name__ == '__main__':
|
|
148
|
+
test_toy()
|
|
124
149
|
test_easy()
|
|
125
150
|
test_easy_2()
|
|
126
151
|
test_ground()
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
from ortools.sat.python import cp_model
|
|
5
|
-
from ortools.sat.python.cp_model import LinearExpr as lxp
|
|
6
|
-
|
|
7
|
-
from puzzle_solver.core.utils import Direction, Pos, get_all_pos, get_next_pos, get_pos, in_bounds, get_char, get_row_pos, get_col_pos
|
|
8
|
-
from puzzle_solver.core.utils_ortools import generic_solve_all, SingleSolution
|
|
9
|
-
from puzzle_solver.core.utils_visualizer import combined_function
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class Board:
|
|
13
|
-
def __init__(self, board: np.array, arith_rows: Optional[np.array] = None, arith_cols: Optional[np.array] = None, force_unique: bool = True):
|
|
14
|
-
assert board.ndim == 2, f'board must be 2d, got {board.ndim}'
|
|
15
|
-
assert all(c.item() in [' ', 'B', 'W'] for c in np.nditer(board)), 'board must contain only space or B'
|
|
16
|
-
self.board = board
|
|
17
|
-
self.V, self.H = board.shape
|
|
18
|
-
if arith_rows is not None:
|
|
19
|
-
assert arith_rows.ndim == 2, f'arith_rows must be 2d, got {arith_rows.ndim}'
|
|
20
|
-
assert arith_rows.shape == (self.V, self.H-1), f'arith_rows must be one column less than board, got {arith_rows.shape} for {board.shape}'
|
|
21
|
-
assert all(isinstance(c.item(), str) and c.item() in [' ', 'x', '='] for c in np.nditer(arith_rows)), 'arith_rows must contain only space, x, or ='
|
|
22
|
-
if arith_cols is not None:
|
|
23
|
-
assert arith_cols.ndim == 2, f'arith_cols must be 2d, got {arith_cols.ndim}'
|
|
24
|
-
assert arith_cols.shape == (self.V-1, self.H), f'arith_cols must be one column and row less than board, got {arith_cols.shape} for {board.shape}'
|
|
25
|
-
assert all(isinstance(c.item(), str) and c.item() in [' ', 'x', '='] for c in np.nditer(arith_cols)), 'arith_cols must contain only space, x, or ='
|
|
26
|
-
self.arith_rows = arith_rows
|
|
27
|
-
self.arith_cols = arith_cols
|
|
28
|
-
self.force_unique = force_unique
|
|
29
|
-
|
|
30
|
-
self.model = cp_model.CpModel()
|
|
31
|
-
self.model_vars: dict[Pos, cp_model.IntVar] = {}
|
|
32
|
-
|
|
33
|
-
self.create_vars()
|
|
34
|
-
self.add_all_constraints()
|
|
35
|
-
|
|
36
|
-
def create_vars(self):
|
|
37
|
-
for pos in get_all_pos(self.V, self.H):
|
|
38
|
-
self.model_vars[pos] = self.model.NewBoolVar(f'{pos}')
|
|
39
|
-
|
|
40
|
-
def add_all_constraints(self):
|
|
41
|
-
for pos in get_all_pos(self.V, self.H): # force clues
|
|
42
|
-
c = get_char(self.board, pos)
|
|
43
|
-
if c == 'B':
|
|
44
|
-
self.model.Add(self.model_vars[pos] == 1)
|
|
45
|
-
elif c == 'W':
|
|
46
|
-
self.model.Add(self.model_vars[pos] == 0)
|
|
47
|
-
# 1. Each row and each column must contain an equal number of white and black circles.
|
|
48
|
-
for row in range(self.V):
|
|
49
|
-
row_vars = [self.model_vars[pos] for pos in get_row_pos(row, self.H)]
|
|
50
|
-
self.model.Add(lxp.sum(row_vars) == len(row_vars) // 2)
|
|
51
|
-
for col in range(self.H):
|
|
52
|
-
col_vars = [self.model_vars[pos] for pos in get_col_pos(col, self.V)]
|
|
53
|
-
self.model.Add(lxp.sum(col_vars) == len(col_vars) // 2)
|
|
54
|
-
# 2. More than two circles of the same color can't be adjacent.
|
|
55
|
-
for pos in get_all_pos(self.V, self.H):
|
|
56
|
-
self.disallow_three_in_a_row(pos, Direction.RIGHT)
|
|
57
|
-
self.disallow_three_in_a_row(pos, Direction.DOWN)
|
|
58
|
-
|
|
59
|
-
# 3. Each row and column is unique.
|
|
60
|
-
if self.force_unique:
|
|
61
|
-
# a list per row
|
|
62
|
-
self.force_unique_double_list([[self.model_vars[pos] for pos in get_row_pos(row, self.H)] for row in range(self.V)])
|
|
63
|
-
# a list per column
|
|
64
|
-
self.force_unique_double_list([[self.model_vars[pos] for pos in get_col_pos(col, self.V)] for col in range(self.H)])
|
|
65
|
-
|
|
66
|
-
# if arithmetic is provided, add constraints for it
|
|
67
|
-
if self.arith_rows is not None:
|
|
68
|
-
assert self.arith_rows.shape == (self.V, self.H-1), f'arith_rows must be one column less than board, got {self.arith_rows.shape} for {self.board.shape}'
|
|
69
|
-
for pos in get_all_pos(self.V, self.H-1):
|
|
70
|
-
c = get_char(self.arith_rows, pos)
|
|
71
|
-
if c == 'x':
|
|
72
|
-
self.model.Add(self.model_vars[pos] != self.model_vars[get_next_pos(pos, Direction.RIGHT)])
|
|
73
|
-
elif c == '=':
|
|
74
|
-
self.model.Add(self.model_vars[pos] == self.model_vars[get_next_pos(pos, Direction.RIGHT)])
|
|
75
|
-
if self.arith_cols is not None:
|
|
76
|
-
assert self.arith_cols.shape == (self.V-1, self.H), f'arith_cols must be one row less than board, got {self.arith_cols.shape} for {self.board.shape}'
|
|
77
|
-
for pos in get_all_pos(self.V-1, self.H):
|
|
78
|
-
c = get_char(self.arith_cols, pos)
|
|
79
|
-
if c == 'x':
|
|
80
|
-
self.model.Add(self.model_vars[pos] != self.model_vars[get_next_pos(pos, Direction.DOWN)])
|
|
81
|
-
elif c == '=':
|
|
82
|
-
self.model.Add(self.model_vars[pos] == self.model_vars[get_next_pos(pos, Direction.DOWN)])
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def disallow_three_in_a_row(self, p1: Pos, direction: Direction):
|
|
86
|
-
p2 = get_next_pos(p1, direction)
|
|
87
|
-
p3 = get_next_pos(p2, direction)
|
|
88
|
-
if any(not in_bounds(p, self.V, self.H) for p in [p1, p2, p3]):
|
|
89
|
-
return
|
|
90
|
-
self.model.AddBoolOr([
|
|
91
|
-
self.model_vars[p1],
|
|
92
|
-
self.model_vars[p2],
|
|
93
|
-
self.model_vars[p3],
|
|
94
|
-
])
|
|
95
|
-
self.model.AddBoolOr([
|
|
96
|
-
self.model_vars[p1].Not(),
|
|
97
|
-
self.model_vars[p2].Not(),
|
|
98
|
-
self.model_vars[p3].Not(),
|
|
99
|
-
])
|
|
100
|
-
|
|
101
|
-
def force_unique_double_list(self, model_vars: list[list[cp_model.IntVar]]):
|
|
102
|
-
if not model_vars or len(model_vars) < 2:
|
|
103
|
-
return
|
|
104
|
-
m = len(model_vars[0])
|
|
105
|
-
assert m <= 61, f"Too many cells for binary encoding in int64: m={m}, model_vars={model_vars}"
|
|
106
|
-
|
|
107
|
-
codes = []
|
|
108
|
-
pow2 = [1 << k for k in range(m)] # weights for bit positions (LSB at index 0)
|
|
109
|
-
for i, line in enumerate(model_vars):
|
|
110
|
-
code = self.model.NewIntVar(0, (1 << m) - 1, f"code_{i}")
|
|
111
|
-
# Sum 2^k * r[k] == code
|
|
112
|
-
self.model.Add(code == sum(pow2[k] * line[k] for k in range(m)))
|
|
113
|
-
codes.append(code)
|
|
114
|
-
|
|
115
|
-
self.model.AddAllDifferent(codes)
|
|
116
|
-
|
|
117
|
-
def solve_and_print(self, verbose: bool = True):
|
|
118
|
-
def board_to_solution(board: Board, solver: cp_model.CpSolverSolutionCallback) -> SingleSolution:
|
|
119
|
-
assignment: dict[Pos, int] = {}
|
|
120
|
-
for pos, var in board.model_vars.items():
|
|
121
|
-
assignment[pos] = solver.Value(var)
|
|
122
|
-
return SingleSolution(assignment=assignment)
|
|
123
|
-
def callback(single_res: SingleSolution):
|
|
124
|
-
print("Solution found")
|
|
125
|
-
print(combined_function(self.V, self.H, is_shaded=lambda r, c: single_res.assignment[get_pos(x=c, y=r)] == 1))
|
|
126
|
-
return generic_solve_all(self, board_to_solution, callback=callback if verbose else None, verbose=verbose)
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/multi_puzzle_solver.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/core/utils_ortools.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/core/utils_visualizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/bridges/bridges.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/filling/filling.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/flip/flip.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/guess/guess.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/inertia/inertia.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/inertia/tsp.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/kakuro/kakuro.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/keen/keen.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/lits/lits.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/magnets/magnets.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/map/map.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/mosaic/mosaic.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/pearl/pearl.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/pipes/pipes.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/range/range.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/singles/singles.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/slant/slant.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/sudoku/sudoku.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tapa/tapa.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tents/tents.py
RENAMED
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/towers/towers.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/tracks/tracks.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/twiddle/twiddle.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/undead/undead.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/unequal/unequal.py
RENAMED
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/puzzles/unruly/unruly.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_puzzle_solver-1.0.8 → multi_puzzle_solver-1.0.9}/src/puzzle_solver/utils/visualizer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|