multi-puzzle-solver 1.0.4__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 (41) hide show
  1. {multi_puzzle_solver-1.0.4.dist-info → multi_puzzle_solver-1.0.6.dist-info}/METADATA +740 -296
  2. multi_puzzle_solver-1.0.6.dist-info/RECORD +73 -0
  3. puzzle_solver/__init__.py +3 -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 +4 -2
  11. puzzle_solver/puzzles/filling/filling.py +11 -34
  12. puzzle_solver/puzzles/galaxies/galaxies.py +4 -2
  13. puzzle_solver/puzzles/heyawake/heyawake.py +6 -2
  14. puzzle_solver/puzzles/kakurasu/kakurasu.py +5 -13
  15. puzzle_solver/puzzles/kakuro/kakuro.py +6 -2
  16. puzzle_solver/puzzles/lits/lits.py +4 -2
  17. puzzle_solver/puzzles/mosaic/mosaic.py +8 -18
  18. puzzle_solver/puzzles/nonograms/nonograms.py +80 -85
  19. puzzle_solver/puzzles/nonograms/nonograms_colored.py +221 -0
  20. puzzle_solver/puzzles/norinori/norinori.py +5 -12
  21. puzzle_solver/puzzles/nurikabe/nurikabe.py +6 -2
  22. puzzle_solver/puzzles/palisade/palisade.py +4 -3
  23. puzzle_solver/puzzles/pearl/pearl.py +15 -27
  24. puzzle_solver/puzzles/pipes/pipes.py +2 -1
  25. puzzle_solver/puzzles/range/range.py +19 -55
  26. puzzle_solver/puzzles/rectangles/rectangles.py +4 -2
  27. puzzle_solver/puzzles/shingoki/shingoki.py +2 -2
  28. puzzle_solver/puzzles/singles/singles.py +6 -2
  29. puzzle_solver/puzzles/slant/slant.py +13 -19
  30. puzzle_solver/puzzles/slitherlink/slitherlink.py +2 -2
  31. puzzle_solver/puzzles/star_battle/star_battle.py +5 -2
  32. puzzle_solver/puzzles/stitches/stitches.py +8 -21
  33. puzzle_solver/puzzles/sudoku/sudoku.py +5 -11
  34. puzzle_solver/puzzles/tapa/tapa.py +6 -2
  35. puzzle_solver/puzzles/tents/tents.py +50 -80
  36. puzzle_solver/puzzles/tracks/tracks.py +19 -66
  37. puzzle_solver/puzzles/unruly/unruly.py +17 -49
  38. puzzle_solver/puzzles/yin_yang/yin_yang.py +3 -10
  39. multi_puzzle_solver-1.0.4.dist-info/RECORD +0 -72
  40. {multi_puzzle_solver-1.0.4.dist-info → multi_puzzle_solver-1.0.6.dist-info}/WHEEL +0 -0
  41. {multi_puzzle_solver-1.0.4.dist-info → multi_puzzle_solver-1.0.6.dist-info}/top_level.txt +0 -0
@@ -2,8 +2,9 @@ from collections import defaultdict
2
2
  import numpy as np
3
3
  from ortools.sat.python import cp_model
4
4
 
5
- from puzzle_solver.core.utils import Pos, get_all_pos, set_char, get_char, Direction, in_bounds, get_next_pos, get_row_pos, get_col_pos, get_opposite_direction
5
+ from puzzle_solver.core.utils import Pos, get_all_pos, get_char, Direction, in_bounds, get_next_pos, get_row_pos, get_col_pos, get_opposite_direction, get_pos
6
6
  from puzzle_solver.core.utils_ortools import force_connected_component, generic_solve_all, SingleSolution
7
+ from puzzle_solver.core.utils_visualizer import combined_function
7
8
 
8
9
 
9
10
  class Board:
@@ -11,8 +12,7 @@ class Board:
11
12
  assert board.ndim == 2, f'board must be 2d, got {board.ndim}'
12
13
  assert all((len(c.item()) == 2) and all(ch in [' ', 'U', 'L', 'D', 'R'] for ch in c.item()) for c in np.nditer(board)), 'board must contain only digits or space'
13
14
  self.board = board
14
- self.V = board.shape[0]
15
- self.H = board.shape[1]
15
+ self.V, self.H = board.shape
16
16
  self.side = side
17
17
  self.top = top
18
18
  self.first_col_start_pos = [p for p in get_col_pos(0, self.V) if 'L' in get_char(self.board, p)]
@@ -33,75 +33,45 @@ class Board:
33
33
  for pos in get_all_pos(self.V, self.H):
34
34
  self.cell_active[pos] = self.model.NewBoolVar(f'{pos}')
35
35
  for direction in Direction:
36
- self.cell_direction[(pos, direction)] = self.model.NewBoolVar(f'{pos}:{direction}')
36
+ next_pos = get_next_pos(pos, direction)
37
+ opposite_direction = get_opposite_direction(direction)
38
+ if (next_pos, opposite_direction) in self.cell_direction:
39
+ self.cell_direction[(pos, direction)] = self.cell_direction[(next_pos, opposite_direction)]
40
+ else:
41
+ self.cell_direction[(pos, direction)] = self.model.NewBoolVar(f'{pos}:{direction}')
37
42
 
38
43
  def add_all_constraints(self):
39
- self.force_hints()
40
- self.force_sides()
41
- self.force_0_or_2_active()
42
- self.force_direction_constraints()
43
- self.force_connected_component()
44
-
45
-
46
- def force_hints(self):
47
44
  # force the already given hints
45
+ str_to_direction = {'U': Direction.UP, 'L': Direction.LEFT, 'D': Direction.DOWN, 'R': Direction.RIGHT}
48
46
  for pos in get_all_pos(self.V, self.H):
49
- c = get_char(self.board, pos)
50
- if 'U' in c:
51
- self.model.Add(self.cell_direction[(pos, Direction.UP)] == 1)
52
- if 'L' in c:
53
- self.model.Add(self.cell_direction[(pos, Direction.LEFT)] == 1)
54
- if 'D' in c:
55
- self.model.Add(self.cell_direction[(pos, Direction.DOWN)] == 1)
56
- if 'R' in c:
57
- self.model.Add(self.cell_direction[(pos, Direction.RIGHT)] == 1)
47
+ for char in get_char(self.board, pos).strip():
48
+ self.model.Add(self.cell_direction[(pos, str_to_direction[char])] == 1)
58
49
 
59
- def force_sides(self):
60
50
  # force the already given sides
61
51
  for i in range(self.V):
62
52
  self.model.Add(sum([self.cell_active[pos] for pos in get_row_pos(i, self.H)]) == self.side[i])
63
53
  for i in range(self.H):
64
54
  self.model.Add(sum([self.cell_active[pos] for pos in get_col_pos(i, self.V)]) == self.top[i])
65
55
 
66
- def force_0_or_2_active(self):
67
56
  # cell active means exactly 2 directions are active, cell not active means no directions are active
68
57
  for pos in get_all_pos(self.V, self.H):
69
58
  s = sum([self.cell_direction[(pos, direction)] for direction in Direction])
70
59
  self.model.Add(s == 2).OnlyEnforceIf(self.cell_active[pos])
71
60
  self.model.Add(s == 0).OnlyEnforceIf(self.cell_active[pos].Not())
72
61
 
73
- def force_direction_constraints(self):
74
- # X having right means the cell to its right has left and so on for all directions
75
- for pos in get_all_pos(self.V, self.H):
76
- right_pos = get_next_pos(pos, Direction.RIGHT)
77
- if in_bounds(right_pos, self.V, self.H):
78
- self.model.Add(self.cell_direction[(pos, Direction.RIGHT)] == self.cell_direction[(right_pos, Direction.LEFT)])
79
- down_pos = get_next_pos(pos, Direction.DOWN)
80
- if in_bounds(down_pos, self.V, self.H):
81
- self.model.Add(self.cell_direction[(pos, Direction.DOWN)] == self.cell_direction[(down_pos, Direction.UP)])
82
- left_pos = get_next_pos(pos, Direction.LEFT)
83
- if in_bounds(left_pos, self.V, self.H):
84
- self.model.Add(self.cell_direction[(pos, Direction.LEFT)] == self.cell_direction[(left_pos, Direction.RIGHT)])
85
- top_pos = get_next_pos(pos, Direction.UP)
86
- if in_bounds(top_pos, self.V, self.H):
87
- self.model.Add(self.cell_direction[(pos, Direction.UP)] == self.cell_direction[(top_pos, Direction.DOWN)])
88
-
89
- # first column cant have L unless it is the start position
90
- for pos in get_col_pos(0, self.V):
62
+ # force borders
63
+ for pos in get_col_pos(0, self.V): # first column cant have L unless it is the start position
91
64
  if pos != self.first_col_start_pos:
92
65
  self.model.Add(self.cell_direction[(pos, Direction.LEFT)] == 0)
93
- # last column cant have R
94
- for pos in get_col_pos(self.H - 1, self.V):
66
+ for pos in get_col_pos(self.H - 1, self.V): # last column cant have R
95
67
  self.model.Add(self.cell_direction[(pos, Direction.RIGHT)] == 0)
96
- # last row cant have D unless it is the end position
97
- for pos in get_row_pos(self.V - 1, self.H):
68
+ for pos in get_row_pos(self.V - 1, self.H): # last row cant have D unless it is the end position
98
69
  if pos != self.last_row_end_pos:
99
70
  self.model.Add(self.cell_direction[(pos, Direction.DOWN)] == 0)
100
- # first row cant have U
101
- for pos in get_row_pos(0, self.H):
71
+ for pos in get_row_pos(0, self.H): # first row cant have U
102
72
  self.model.Add(self.cell_direction[(pos, Direction.UP)] == 0)
103
73
 
104
- def force_connected_component(self):
74
+ # force single connected component
105
75
  def is_neighbor(pd1: tuple[Pos, Direction], pd2: tuple[Pos, Direction]) -> bool:
106
76
  p1, d1 = pd1
107
77
  p2, d2 = pd2
@@ -112,30 +82,13 @@ class Board:
112
82
  return False
113
83
  force_connected_component(self.model, self.cell_direction, is_neighbor=is_neighbor)
114
84
 
115
-
116
-
117
-
118
-
119
85
  def solve_and_print(self, verbose: bool = True):
120
86
  def board_to_solution(board: Board, solver: cp_model.CpSolverSolutionCallback) -> SingleSolution:
121
87
  assignment: dict[Pos, str] = defaultdict(str)
122
88
  for (pos, direction), var in board.cell_direction.items():
123
89
  assignment[pos] += direction.name[0] if solver.BooleanValue(var) else ''
124
- for pos in get_all_pos(self.V, self.H):
125
- if len(assignment[pos]) == 0:
126
- assignment[pos] = ' '
127
- else:
128
- assignment[pos] = ''.join(sorted(assignment[pos]))
129
90
  return SingleSolution(assignment=assignment)
130
91
  def callback(single_res: SingleSolution):
131
92
  print("Solution found")
132
- print(single_res.assignment)
133
- res = np.full((self.V, self.H), ' ', dtype=object)
134
- pretty_dict = {'DU': '┃ ', 'LR': '━━', 'DL': '━┒', 'DR': '┏━', 'RU': '┗━', 'LU': '━┛', ' ': ' '}
135
- for pos in get_all_pos(self.V, self.H):
136
- c = get_char(self.board, pos)
137
- c = single_res.assignment[pos]
138
- c = pretty_dict[c]
139
- set_char(res, pos, c)
140
- print(res)
93
+ print(combined_function(self.V, self.H, show_grid=False, special_content=lambda r, c: single_res.assignment[get_pos(x=c, y=r)].strip(), center_char=lambda r, c: '.', text_on_shaded_cells=False))
141
94
  return generic_solve_all(self, board_to_solution, callback=callback if verbose else None, verbose=verbose, max_solutions=20)
@@ -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,72 +0,0 @@
1
- puzzle_solver/__init__.py,sha256=j1j97BCo0zoRY1QF6G4248BMWQCYB9TV55hbkbxbDWI,5178
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/connect_the_dots/connect_the_dots.py,sha256=PvxwoIUDHITxo51uD_JGoFcW33vf0YXTsvlm-gUjMNY,2610
16
- puzzle_solver/puzzles/dominosa/dominosa.py,sha256=Nmb7pn8U27QJwGy9F3wo8ylqo2_U51OAo3GN2soaNpc,7195
17
- puzzle_solver/puzzles/filling/filling.py,sha256=R8UIbztk3zNCeNbVClBJoKZHKeHwK_pesjGmMaEEQO0,5536
18
- puzzle_solver/puzzles/flip/flip.py,sha256=ZngJLUhRNc7qqo2wtNLdMPx4u9w9JTUge27PmdXyDCw,3985
19
- puzzle_solver/puzzles/flood_it/flood_it.py,sha256=jnCtH1sZIt6K4hbQDSsiM1Cd8FjQNP7cfw2ObUW5fEQ,7948
20
- puzzle_solver/puzzles/flood_it/parse_map/parse_map.py,sha256=m7gcpvN3THZdYLowdR_Jwx3HyttaV4K2DqrX_U7uFqU,8209
21
- puzzle_solver/puzzles/galaxies/galaxies.py,sha256=CTSCNqRR35QT7qBiYdXYu9GLnDJmI9Cfd4C5t4_Kqhg,5535
22
- puzzle_solver/puzzles/galaxies/parse_map/parse_map.py,sha256=XmFqVN_oRfq9AZFWy5ViUJ2Szjgx-srrRkFPJXEEyFo,9358
23
- puzzle_solver/puzzles/guess/guess.py,sha256=MpyrF6YVu0S1fzX-BllwxGKRGacWJpeLbNn5GetuEyo,10792
24
- puzzle_solver/puzzles/heyawake/heyawake.py,sha256=L_y44dHArOvO_tDyO35dwkvqdk9eEGItO7n4FDfzNDc,5586
25
- puzzle_solver/puzzles/inertia/inertia.py,sha256=-Y5fr7aK20zwmGHsZql7pYCq1kyMZglvkVZ6uIDf1HA,5658
26
- puzzle_solver/puzzles/inertia/tsp.py,sha256=mAhlSjCWespASeN8uLZ0JkYDw-ZqFEpal6NM-ubpCXw,15313
27
- puzzle_solver/puzzles/inertia/parse_map/parse_map.py,sha256=x0d64gTBd0HC2lO5uOpX2VKWfwj8rRiz0mQM_lqNmWs,8457
28
- puzzle_solver/puzzles/kakurasu/kakurasu.py,sha256=VNGMJnBHDi6WkghLObRLhUvkmrPaGphTTUDMC0TkQvQ,2064
29
- puzzle_solver/puzzles/kakuro/kakuro.py,sha256=m22Ju-V2BdQl2Ng_pjVUSrxPCtIfqezdpebutURlhvg,4348
30
- puzzle_solver/puzzles/keen/keen.py,sha256=adSA_pc1m6F6jV7a-PpQxdci1bv4psCNRNt9hMIQdSY,5034
31
- puzzle_solver/puzzles/light_up/light_up.py,sha256=iSA1rjZMFsnI0V0Nxivxox4qZkB7PvUrROSHXcoUXds,4541
32
- puzzle_solver/puzzles/lits/lits.py,sha256=-iKgsdfFHA0aOQD8QCjSCXabQaHFaHN5idk6LBvgqRc,7166
33
- puzzle_solver/puzzles/magnets/magnets.py,sha256=-Wl49JD_PKeq735zQVMQ3XSQX6gdHiY-7PKw-Sh16jw,6474
34
- puzzle_solver/puzzles/map/map.py,sha256=sxc57tapB8Tsgam-yoDitln1o-EB_SbIYvO6WEYy3us,2582
35
- puzzle_solver/puzzles/minesweeper/minesweeper.py,sha256=gSdFsuZ-KrwVxgI1HF2q_pYleZ6vBm9jjRTFlboVnLY,5871
36
- puzzle_solver/puzzles/mosaic/mosaic.py,sha256=QX_nVpVKQg8OfaUcqFk9tKqsDyVqvZc6-XWvfI3YcSw,2175
37
- puzzle_solver/puzzles/nonograms/nonograms.py,sha256=dTKfMwBL49hW3bNd34ETXW7lBRPuQeSPNSCHqHmfybg,6066
38
- puzzle_solver/puzzles/norinori/norinori.py,sha256=iwJ2UgQJfx6JLYPQqJHdg1GJ8IBAjMDOtNybhO1jmNc,4968
39
- puzzle_solver/puzzles/nurikabe/nurikabe.py,sha256=3cbW7X4kAMQK8PkH_t65fzT5cI0O6tWWOqpQUVyuGT4,6501
40
- puzzle_solver/puzzles/palisade/palisade.py,sha256=T-LXlaLU5OwUQ24QWJWhBUFUktg0qDODTilNmBaXs4I,5014
41
- puzzle_solver/puzzles/pearl/pearl.py,sha256=OhzpMYpxqvR3GCd5NH4ETT0NO4X753kRi6p5omYLChM,6798
42
- puzzle_solver/puzzles/pipes/pipes.py,sha256=SPPgmYXeqjdzijLqdIb_TtlGmxzIad6MHQ31pyDcgUc,4448
43
- puzzle_solver/puzzles/range/range.py,sha256=q0J3crlGfjYZSA6Dh4iMCwP_gRMWid-_8KPgggOrFKk,4410
44
- puzzle_solver/puzzles/rectangles/rectangles.py,sha256=MgOhZJGr9DVHb9bB8EAuwus0_8frBqRWqMwrOvMezHQ,6918
45
- puzzle_solver/puzzles/shakashaka/shakashaka.py,sha256=PRpg_qI7XA3ysAo_g1TRJsT3VwB5Vial2UcFyBOMwKQ,9571
46
- puzzle_solver/puzzles/shingoki/shingoki.py,sha256=heMuL9sm3jBegItjnqX05ttmDNiHSLB77BRljpeLLWk,7417
47
- puzzle_solver/puzzles/signpost/signpost.py,sha256=38LlMvP5Fx4qrTXmw4aNCt3yUbG3fhdSk6-YXmhAHFg,3861
48
- puzzle_solver/puzzles/singles/singles.py,sha256=KKn_Yl-eW874Bl1UmmcqoQ5vhNiO1JbM7fxKczOV5M4,2847
49
- puzzle_solver/puzzles/slant/slant.py,sha256=xF-N4PuXYfx638NP1f1mi6YncIZB4mLtXtdS79XyPbg,6122
50
- puzzle_solver/puzzles/slant/parse_map/parse_map.py,sha256=8thQxWbq0qjehKb2VzgUP22PGj-9n9djwbt3LGMVLJw,4811
51
- puzzle_solver/puzzles/slitherlink/slitherlink.py,sha256=JpyNQk8K4nUziwWKxSvWEkF1RRBGLnCppCWK1Yf5bt0,7052
52
- puzzle_solver/puzzles/star_battle/star_battle.py,sha256=hFV5IKPQDWrIWr50YpiOS9VF2kUScDQpvAGvZKBwuyM,3937
53
- puzzle_solver/puzzles/star_battle/star_battle_shapeless.py,sha256=lj05V0Y3A3NjMo1boMkPIwBhMtm6SWydjgAMeCf5EIo,225
54
- puzzle_solver/puzzles/stitches/stitches.py,sha256=bb5JXyclkbKq350MQ9d8AuGteQwSF8knaJ0DU9M92Uw,6515
55
- puzzle_solver/puzzles/stitches/parse_map/parse_map.py,sha256=b21SQvlnDM6wOl_1iUhZ7X6akpBZoOnj3kEzImBCh8Q,10497
56
- puzzle_solver/puzzles/sudoku/sudoku.py,sha256=rLq0N34v3Hb10CiptXtKxX-37OlQIyjIle9Es1FAtpM,13378
57
- puzzle_solver/puzzles/tapa/tapa.py,sha256=TsOQhnEvlC1JxaWiEjQg2KxRXJR49GrN71DsMvPpia8,5337
58
- puzzle_solver/puzzles/tents/tents.py,sha256=jccUXWA7KWAtPKpVJJYNI6masTYWQgx0eitcQw0-6Fc,6281
59
- puzzle_solver/puzzles/thermometers/thermometers.py,sha256=bGcVmpPeqL5AJtj8jkK8gYThzv9aGCd_QrWEiYBCA2s,4011
60
- puzzle_solver/puzzles/towers/towers.py,sha256=OLyTf9nTFR5L32-S_fhVyBmpz4i5YUNJotwOwbw_Fjg,6500
61
- puzzle_solver/puzzles/tracks/tracks.py,sha256=98xds9SKNqtOLFTRUX_KSMC7XYmZo567LOFeqotVQaM,7237
62
- puzzle_solver/puzzles/twiddle/twiddle.py,sha256=3gPoeD0DoiiZbIhtptdXFldO_t1QShL6IxkDqJMzjkk,5446
63
- puzzle_solver/puzzles/undead/undead.py,sha256=IGFQysgoaKZH8rKjqlrkoHsH28ve4_hKor2f0QOsWY0,6596
64
- puzzle_solver/puzzles/unequal/unequal.py,sha256=ExY2XDCrqROCDpRLfHo8uVr1zuli1QvbCdNCiDhlCac,6978
65
- puzzle_solver/puzzles/unruly/unruly.py,sha256=xwOUpC12uHbmlDj2guN60VaaHpLr1Y-WmMD5TKeHbZE,3826
66
- puzzle_solver/puzzles/yin_yang/yin_yang.py,sha256=5WixT_7K1HwfQ_dWbuBlQfpU8p69zB2KvOg32XJ8vno,5255
67
- puzzle_solver/puzzles/yin_yang/parse_map/parse_map.py,sha256=drjfoHqmFf6U-ZQUwrBbfGINRxDQpgbvy4U3D9QyMhM,6617
68
- puzzle_solver/utils/visualizer.py,sha256=T2g5We9J3tkhyXWoN2OrIDIJDjt6w5sDd2ksOub0ZI8,6819
69
- multi_puzzle_solver-1.0.4.dist-info/METADATA,sha256=JSM2UZgGRSx0oipFuvGnNmKLfXWWfZGxgVWHNtWIH3I,372540
70
- multi_puzzle_solver-1.0.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
71
- multi_puzzle_solver-1.0.4.dist-info/top_level.txt,sha256=exwVUQa-anK9vYrpKzBPvH8bX43iElWI4VeNiAyBGJY,14
72
- multi_puzzle_solver-1.0.4.dist-info/RECORD,,