multi-puzzle-solver 1.0.3__py3-none-any.whl → 1.0.6__py3-none-any.whl

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.

Files changed (43) hide show
  1. {multi_puzzle_solver-1.0.3.dist-info → multi_puzzle_solver-1.0.6.dist-info}/METADATA +1024 -387
  2. multi_puzzle_solver-1.0.6.dist-info/RECORD +73 -0
  3. puzzle_solver/__init__.py +7 -1
  4. puzzle_solver/core/utils.py +17 -1
  5. puzzle_solver/core/utils_visualizer.py +257 -201
  6. puzzle_solver/puzzles/aquarium/aquarium.py +8 -23
  7. puzzle_solver/puzzles/battleships/battleships.py +39 -53
  8. puzzle_solver/puzzles/binairo/binairo.py +2 -2
  9. puzzle_solver/puzzles/black_box/black_box.py +6 -70
  10. puzzle_solver/puzzles/connect_the_dots/connect_the_dots.py +50 -0
  11. puzzle_solver/puzzles/filling/filling.py +11 -34
  12. puzzle_solver/puzzles/flood_it/parse_map/parse_map.py +0 -1
  13. puzzle_solver/puzzles/galaxies/galaxies.py +110 -110
  14. puzzle_solver/puzzles/heyawake/heyawake.py +6 -2
  15. puzzle_solver/puzzles/kakurasu/kakurasu.py +5 -13
  16. puzzle_solver/puzzles/kakuro/kakuro.py +6 -2
  17. puzzle_solver/puzzles/lits/lits.py +6 -4
  18. puzzle_solver/puzzles/mosaic/mosaic.py +8 -18
  19. puzzle_solver/puzzles/nonograms/nonograms.py +80 -85
  20. puzzle_solver/puzzles/nonograms/nonograms_colored.py +221 -0
  21. puzzle_solver/puzzles/norinori/norinori.py +5 -10
  22. puzzle_solver/puzzles/nurikabe/nurikabe.py +6 -2
  23. puzzle_solver/puzzles/palisade/palisade.py +4 -3
  24. puzzle_solver/puzzles/pearl/pearl.py +15 -27
  25. puzzle_solver/puzzles/pipes/pipes.py +2 -1
  26. puzzle_solver/puzzles/range/range.py +19 -55
  27. puzzle_solver/puzzles/rectangles/rectangles.py +4 -2
  28. puzzle_solver/puzzles/shingoki/shingoki.py +2 -2
  29. puzzle_solver/puzzles/singles/singles.py +6 -2
  30. puzzle_solver/puzzles/slant/slant.py +13 -19
  31. puzzle_solver/puzzles/slitherlink/slitherlink.py +2 -2
  32. puzzle_solver/puzzles/star_battle/star_battle.py +13 -7
  33. puzzle_solver/puzzles/stitches/stitches.py +8 -21
  34. puzzle_solver/puzzles/sudoku/sudoku.py +5 -11
  35. puzzle_solver/puzzles/tapa/tapa.py +6 -2
  36. puzzle_solver/puzzles/tents/tents.py +50 -80
  37. puzzle_solver/puzzles/tracks/tracks.py +19 -66
  38. puzzle_solver/puzzles/twiddle/twiddle.py +112 -0
  39. puzzle_solver/puzzles/unruly/unruly.py +17 -49
  40. puzzle_solver/puzzles/yin_yang/yin_yang.py +3 -10
  41. multi_puzzle_solver-1.0.3.dist-info/RECORD +0 -70
  42. {multi_puzzle_solver-1.0.3.dist-info → multi_puzzle_solver-1.0.6.dist-info}/WHEEL +0 -0
  43. {multi_puzzle_solver-1.0.3.dist-info → multi_puzzle_solver-1.0.6.dist-info}/top_level.txt +0 -0
@@ -2,35 +2,17 @@ import numpy as np
2
2
  from ortools.sat.python import cp_model
3
3
  from ortools.sat.python.cp_model import LinearExpr as lxp
4
4
 
5
- from puzzle_solver.core.utils import Pos, get_all_pos, set_char, get_char, get_row_pos, get_col_pos, in_bounds, Direction, get_next_pos
5
+ from puzzle_solver.core.utils import Pos, get_all_pos, get_char, get_row_pos, get_col_pos, in_bounds, Direction, get_next_pos, get_pos
6
+ from puzzle_solver.core.utils_visualizer import combined_function
6
7
  from puzzle_solver.core.utils_ortools import generic_solve_all, SingleSolution
7
8
 
8
9
 
9
- def get_3_consecutive_horiz_and_vert(pos: Pos, V: int, H: int) -> tuple[list[Pos], list[Pos]]:
10
- """Get 3 consecutive squares, horizontally and vertically, from the given position."""
11
- horiz = []
12
- vert = []
13
- cur_pos = pos
14
- for _ in range(3):
15
- if in_bounds(cur_pos, V, H):
16
- horiz.append(cur_pos)
17
- cur_pos = get_next_pos(cur_pos, Direction.RIGHT)
18
- cur_pos = pos
19
- for _ in range(3):
20
- if in_bounds(cur_pos, V, H):
21
- vert.append(cur_pos)
22
- cur_pos = get_next_pos(cur_pos, Direction.DOWN)
23
- return horiz, vert
24
-
25
-
26
10
  class Board:
27
11
  def __init__(self, board: np.array):
28
12
  assert board.ndim == 2, f'board must be 2d, got {board.ndim}'
29
- assert board.shape[0] % 2 == 0, 'board must have even number of rows'
30
- assert board.shape[1] % 2 == 0, 'board must have even number of columns'
13
+ assert board.shape[0] % 2 == 0 and board.shape[1] % 2 == 0, 'board must have even number of rows and columns'
31
14
  self.board = board
32
- self.V = board.shape[0]
33
- self.H = board.shape[1]
15
+ self.V, self.H = board.shape
34
16
  self.model = cp_model.CpModel()
35
17
  self.model_vars: dict[Pos, cp_model.IntVar] = {}
36
18
 
@@ -42,24 +24,19 @@ class Board:
42
24
  self.model_vars[pos] = self.model.NewBoolVar(f'{pos}')
43
25
 
44
26
  def add_all_constraints(self):
45
- # some cells are already filled
46
27
  for pos in get_all_pos(self.V, self.H):
28
+ # enforce hints
47
29
  c = get_char(self.board, pos)
48
- if c == ' ':
49
- continue
50
- v = 1 if c == 'B' else 0
51
- self.model.Add(self.model_vars[pos] == v)
52
- # no three consecutive squares, horizontally or vertically, are the same colour
53
- for pos in get_all_pos(self.V, self.H):
54
- horiz, vert = get_3_consecutive_horiz_and_vert(pos, self.V, self.H)
55
- if len(horiz) == 3:
56
- horiz = [self.model_vars[h] for h in horiz]
57
- self.model.Add(lxp.Sum(horiz) != 0)
58
- self.model.Add(lxp.Sum(horiz) != 3)
59
- if len(vert) == 3:
60
- vert = [self.model_vars[v] for v in vert]
61
- self.model.Add(lxp.Sum(vert) != 0)
62
- self.model.Add(lxp.Sum(vert) != 3)
30
+ if c.strip():
31
+ self.model.Add(self.model_vars[pos] == (c.strip() == 'B'))
32
+ # no three consecutive squares, horizontally or vertically, are the same colour
33
+ for direction in [Direction.RIGHT, Direction.DOWN]:
34
+ var_list = [pos]
35
+ for _ in range(2):
36
+ var_list.append(get_next_pos(var_list[-1], direction))
37
+ if all(in_bounds(v, self.V, self.H) for v in var_list):
38
+ self.model.Add(lxp.Sum([self.model_vars[v] for v in var_list]) != 0)
39
+ self.model.Add(lxp.Sum([self.model_vars[v] for v in var_list]) != 3)
63
40
  # each row and column contains the same number of black and white squares.
64
41
  for col in range(self.H):
65
42
  var_list = [self.model_vars[pos] for pos in get_col_pos(col, self.V)]
@@ -70,17 +47,8 @@ class Board:
70
47
 
71
48
  def solve_and_print(self, verbose: bool = True):
72
49
  def board_to_solution(board: Board, solver: cp_model.CpSolverSolutionCallback) -> SingleSolution:
73
- assignment: dict[Pos, int] = {}
74
- for pos, var in board.model_vars.items():
75
- assignment[pos] = solver.Value(var)
76
- return SingleSolution(assignment=assignment)
50
+ return SingleSolution(assignment={pos: solver.Value(var) for pos, var in board.model_vars.items()})
77
51
  def callback(single_res: SingleSolution):
78
52
  print("Solution found")
79
- res = np.full((self.V, self.H), ' ', dtype=object)
80
- for pos in get_all_pos(self.V, self.H):
81
- c = get_char(self.board, pos)
82
- if c == ' ':
83
- c = 'B' if single_res.assignment[pos] == 1 else 'W'
84
- set_char(res, pos, c)
85
- print(res)
53
+ print(combined_function(self.V, self.H, is_shaded=lambda r, c: single_res.assignment[get_pos(x=c, y=r)] == 1))
86
54
  return generic_solve_all(self, board_to_solution, callback=callback if verbose else None, verbose=verbose)
@@ -3,8 +3,9 @@ import numpy as np
3
3
  from ortools.sat.python import cp_model
4
4
  from ortools.sat.python.cp_model import LinearExpr as lxp
5
5
 
6
- from puzzle_solver.core.utils import Pos, get_all_pos, set_char, get_char, in_bounds, Direction, get_next_pos, get_pos
6
+ from puzzle_solver.core.utils import Pos, get_all_pos, get_char, in_bounds, Direction, get_next_pos, get_pos
7
7
  from puzzle_solver.core.utils_ortools import and_constraint, generic_solve_all, SingleSolution, force_connected_component
8
+ from puzzle_solver.core.utils_visualizer import combined_function
8
9
 
9
10
 
10
11
  class Board:
@@ -98,13 +99,5 @@ class Board:
98
99
  return SingleSolution(assignment=assignment)
99
100
  def callback(single_res: SingleSolution):
100
101
  print("Solution found")
101
- res = np.full((self.V, self.H), ' ', dtype=object)
102
- for pos in get_all_pos(self.V, self.H):
103
- c = get_char(self.board, pos)
104
- c = single_res.assignment[pos]
105
- set_char(res, pos, c)
106
- print('[')
107
- for row in res:
108
- print(" [ '" + "', '".join(row.tolist()) + "' ],")
109
- print(']')
102
+ print(combined_function(self.V, self.H, is_shaded=lambda r, c: single_res.assignment[get_pos(x=c, y=r)] == 'B'))
110
103
  return generic_solve_all(self, board_to_solution, callback=callback if verbose else None, verbose=verbose)
@@ -1,70 +0,0 @@
1
- puzzle_solver/__init__.py,sha256=g1rzu3qSDD7oz46Els59z3HCxigzjkkFmoG244ymWro,4966
2
- puzzle_solver/core/utils.py,sha256=XBW5j-IwtJMPMP-ycmY6SqRCM1NOVl5O6UeoGqNj618,8153
3
- puzzle_solver/core/utils_ortools.py,sha256=ACV3HgKWpEUTt1lpqsPryK1DeZpu7kdWQKEWTLJ2tfs,10384
4
- puzzle_solver/core/utils_visualizer.py,sha256=ymuhF75uwJbNhN8XVDYEPqw6sPKoqRaaxlhGeHtXpLs,20201
5
- puzzle_solver/puzzles/aquarium/aquarium.py,sha256=BUfkAS2d9eG3TdMoe1cOGGeNYgKUebRvn-z9nsC9gvE,5708
6
- puzzle_solver/puzzles/battleships/battleships.py,sha256=RuYCrs4j0vUjlU139NRYYP-uNPAgO0V7hAzbsHrRwD8,7446
7
- puzzle_solver/puzzles/binairo/binairo.py,sha256=NmVPIoyVCoMLaSFhsN0TcJQYvav9hi4hSwoAVirYhDU,6835
8
- puzzle_solver/puzzles/binairo/binairo_plus.py,sha256=TvLG3olwANtft3LuCF-y4OofpU9PNa4IXDqgZqsD-g0,267
9
- puzzle_solver/puzzles/black_box/black_box.py,sha256=EiCVkbhUP0x94otvQirv7MrggTu0ok8MIUPbxv6jkIU,15544
10
- puzzle_solver/puzzles/bridges/bridges.py,sha256=QwOhZyO5urbatkNyPmQxZ_lGM01ZejndMr_eoiBkr7g,5394
11
- puzzle_solver/puzzles/chess_range/chess_melee.py,sha256=D-_Oi8OyxsVe1j3dIKYwRlxgeb3NWLmDWGcv-oclY0c,195
12
- puzzle_solver/puzzles/chess_range/chess_range.py,sha256=_VHlpUPnqeBstvSIt9RtTV-w2etSK7UrEHg6sErNqtU,21068
13
- puzzle_solver/puzzles/chess_range/chess_solo.py,sha256=ByDfcRsk5FVmFicpU_DpLoLTJ99Kr___vX4y8ln8_EQ,400
14
- puzzle_solver/puzzles/chess_sequence/chess_sequence.py,sha256=6ap3Wouf2PxHV4P56B9ol1QT98Ym6VHaxorQZWl6LnY,13692
15
- puzzle_solver/puzzles/dominosa/dominosa.py,sha256=Nmb7pn8U27QJwGy9F3wo8ylqo2_U51OAo3GN2soaNpc,7195
16
- puzzle_solver/puzzles/filling/filling.py,sha256=R8UIbztk3zNCeNbVClBJoKZHKeHwK_pesjGmMaEEQO0,5536
17
- puzzle_solver/puzzles/flip/flip.py,sha256=ZngJLUhRNc7qqo2wtNLdMPx4u9w9JTUge27PmdXyDCw,3985
18
- puzzle_solver/puzzles/flood_it/flood_it.py,sha256=jnCtH1sZIt6K4hbQDSsiM1Cd8FjQNP7cfw2ObUW5fEQ,7948
19
- puzzle_solver/puzzles/flood_it/parse_map/parse_map.py,sha256=0aw1TbiyxknY2hUAXaP3nXqT6I6mT9BIiERJSCj57xw,8245
20
- puzzle_solver/puzzles/galaxies/galaxies.py,sha256=36X9jaQfvLIWFkBY1VZH6I59eCDkc77U06NDtKRUECY,5571
21
- puzzle_solver/puzzles/galaxies/parse_map/parse_map.py,sha256=XmFqVN_oRfq9AZFWy5ViUJ2Szjgx-srrRkFPJXEEyFo,9358
22
- puzzle_solver/puzzles/guess/guess.py,sha256=MpyrF6YVu0S1fzX-BllwxGKRGacWJpeLbNn5GetuEyo,10792
23
- puzzle_solver/puzzles/heyawake/heyawake.py,sha256=L_y44dHArOvO_tDyO35dwkvqdk9eEGItO7n4FDfzNDc,5586
24
- puzzle_solver/puzzles/inertia/inertia.py,sha256=-Y5fr7aK20zwmGHsZql7pYCq1kyMZglvkVZ6uIDf1HA,5658
25
- puzzle_solver/puzzles/inertia/tsp.py,sha256=mAhlSjCWespASeN8uLZ0JkYDw-ZqFEpal6NM-ubpCXw,15313
26
- puzzle_solver/puzzles/inertia/parse_map/parse_map.py,sha256=x0d64gTBd0HC2lO5uOpX2VKWfwj8rRiz0mQM_lqNmWs,8457
27
- puzzle_solver/puzzles/kakurasu/kakurasu.py,sha256=VNGMJnBHDi6WkghLObRLhUvkmrPaGphTTUDMC0TkQvQ,2064
28
- puzzle_solver/puzzles/kakuro/kakuro.py,sha256=m22Ju-V2BdQl2Ng_pjVUSrxPCtIfqezdpebutURlhvg,4348
29
- puzzle_solver/puzzles/keen/keen.py,sha256=adSA_pc1m6F6jV7a-PpQxdci1bv4psCNRNt9hMIQdSY,5034
30
- puzzle_solver/puzzles/light_up/light_up.py,sha256=iSA1rjZMFsnI0V0Nxivxox4qZkB7PvUrROSHXcoUXds,4541
31
- puzzle_solver/puzzles/lits/lits.py,sha256=3fPIkhAIUz8JokcfaE_ZM3b0AFEnf5xPzGJ2qnm8SWY,7099
32
- puzzle_solver/puzzles/magnets/magnets.py,sha256=-Wl49JD_PKeq735zQVMQ3XSQX6gdHiY-7PKw-Sh16jw,6474
33
- puzzle_solver/puzzles/map/map.py,sha256=sxc57tapB8Tsgam-yoDitln1o-EB_SbIYvO6WEYy3us,2582
34
- puzzle_solver/puzzles/minesweeper/minesweeper.py,sha256=gSdFsuZ-KrwVxgI1HF2q_pYleZ6vBm9jjRTFlboVnLY,5871
35
- puzzle_solver/puzzles/mosaic/mosaic.py,sha256=QX_nVpVKQg8OfaUcqFk9tKqsDyVqvZc6-XWvfI3YcSw,2175
36
- puzzle_solver/puzzles/nonograms/nonograms.py,sha256=dTKfMwBL49hW3bNd34ETXW7lBRPuQeSPNSCHqHmfybg,6066
37
- puzzle_solver/puzzles/norinori/norinori.py,sha256=qR7V7NbZRN_ME90R2jL47AkGik1CY6JlAPhLBMXP2Gw,4714
38
- puzzle_solver/puzzles/nurikabe/nurikabe.py,sha256=3cbW7X4kAMQK8PkH_t65fzT5cI0O6tWWOqpQUVyuGT4,6501
39
- puzzle_solver/puzzles/palisade/palisade.py,sha256=T-LXlaLU5OwUQ24QWJWhBUFUktg0qDODTilNmBaXs4I,5014
40
- puzzle_solver/puzzles/pearl/pearl.py,sha256=OhzpMYpxqvR3GCd5NH4ETT0NO4X753kRi6p5omYLChM,6798
41
- puzzle_solver/puzzles/pipes/pipes.py,sha256=SPPgmYXeqjdzijLqdIb_TtlGmxzIad6MHQ31pyDcgUc,4448
42
- puzzle_solver/puzzles/range/range.py,sha256=q0J3crlGfjYZSA6Dh4iMCwP_gRMWid-_8KPgggOrFKk,4410
43
- puzzle_solver/puzzles/rectangles/rectangles.py,sha256=MgOhZJGr9DVHb9bB8EAuwus0_8frBqRWqMwrOvMezHQ,6918
44
- puzzle_solver/puzzles/shakashaka/shakashaka.py,sha256=PRpg_qI7XA3ysAo_g1TRJsT3VwB5Vial2UcFyBOMwKQ,9571
45
- puzzle_solver/puzzles/shingoki/shingoki.py,sha256=heMuL9sm3jBegItjnqX05ttmDNiHSLB77BRljpeLLWk,7417
46
- puzzle_solver/puzzles/signpost/signpost.py,sha256=38LlMvP5Fx4qrTXmw4aNCt3yUbG3fhdSk6-YXmhAHFg,3861
47
- puzzle_solver/puzzles/singles/singles.py,sha256=KKn_Yl-eW874Bl1UmmcqoQ5vhNiO1JbM7fxKczOV5M4,2847
48
- puzzle_solver/puzzles/slant/slant.py,sha256=xF-N4PuXYfx638NP1f1mi6YncIZB4mLtXtdS79XyPbg,6122
49
- puzzle_solver/puzzles/slant/parse_map/parse_map.py,sha256=8thQxWbq0qjehKb2VzgUP22PGj-9n9djwbt3LGMVLJw,4811
50
- puzzle_solver/puzzles/slitherlink/slitherlink.py,sha256=JpyNQk8K4nUziwWKxSvWEkF1RRBGLnCppCWK1Yf5bt0,7052
51
- puzzle_solver/puzzles/star_battle/star_battle.py,sha256=IX6w4H3sifN01kPPtrAVRCK0Nl_xlXXSHvJKw8K1EuE,3718
52
- puzzle_solver/puzzles/star_battle/star_battle_shapeless.py,sha256=lj05V0Y3A3NjMo1boMkPIwBhMtm6SWydjgAMeCf5EIo,225
53
- puzzle_solver/puzzles/stitches/stitches.py,sha256=bb5JXyclkbKq350MQ9d8AuGteQwSF8knaJ0DU9M92Uw,6515
54
- puzzle_solver/puzzles/stitches/parse_map/parse_map.py,sha256=b21SQvlnDM6wOl_1iUhZ7X6akpBZoOnj3kEzImBCh8Q,10497
55
- puzzle_solver/puzzles/sudoku/sudoku.py,sha256=rLq0N34v3Hb10CiptXtKxX-37OlQIyjIle9Es1FAtpM,13378
56
- puzzle_solver/puzzles/tapa/tapa.py,sha256=TsOQhnEvlC1JxaWiEjQg2KxRXJR49GrN71DsMvPpia8,5337
57
- puzzle_solver/puzzles/tents/tents.py,sha256=jccUXWA7KWAtPKpVJJYNI6masTYWQgx0eitcQw0-6Fc,6281
58
- puzzle_solver/puzzles/thermometers/thermometers.py,sha256=bGcVmpPeqL5AJtj8jkK8gYThzv9aGCd_QrWEiYBCA2s,4011
59
- puzzle_solver/puzzles/towers/towers.py,sha256=OLyTf9nTFR5L32-S_fhVyBmpz4i5YUNJotwOwbw_Fjg,6500
60
- puzzle_solver/puzzles/tracks/tracks.py,sha256=98xds9SKNqtOLFTRUX_KSMC7XYmZo567LOFeqotVQaM,7237
61
- puzzle_solver/puzzles/undead/undead.py,sha256=IGFQysgoaKZH8rKjqlrkoHsH28ve4_hKor2f0QOsWY0,6596
62
- puzzle_solver/puzzles/unequal/unequal.py,sha256=ExY2XDCrqROCDpRLfHo8uVr1zuli1QvbCdNCiDhlCac,6978
63
- puzzle_solver/puzzles/unruly/unruly.py,sha256=xwOUpC12uHbmlDj2guN60VaaHpLr1Y-WmMD5TKeHbZE,3826
64
- puzzle_solver/puzzles/yin_yang/yin_yang.py,sha256=5WixT_7K1HwfQ_dWbuBlQfpU8p69zB2KvOg32XJ8vno,5255
65
- puzzle_solver/puzzles/yin_yang/parse_map/parse_map.py,sha256=drjfoHqmFf6U-ZQUwrBbfGINRxDQpgbvy4U3D9QyMhM,6617
66
- puzzle_solver/utils/visualizer.py,sha256=T2g5We9J3tkhyXWoN2OrIDIJDjt6w5sDd2ksOub0ZI8,6819
67
- multi_puzzle_solver-1.0.3.dist-info/METADATA,sha256=XxSydqNr_sbU-cmZcqvrI5ODi-vrpx7KuLWNmK9q4W4,350517
68
- multi_puzzle_solver-1.0.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
69
- multi_puzzle_solver-1.0.3.dist-info/top_level.txt,sha256=exwVUQa-anK9vYrpKzBPvH8bX43iElWI4VeNiAyBGJY,14
70
- multi_puzzle_solver-1.0.3.dist-info/RECORD,,