multi-puzzle-solver 0.9.26__py3-none-any.whl → 0.9.30__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.
- {multi_puzzle_solver-0.9.26.dist-info → multi_puzzle_solver-0.9.30.dist-info}/METADATA +982 -41
- {multi_puzzle_solver-0.9.26.dist-info → multi_puzzle_solver-0.9.30.dist-info}/RECORD +20 -12
- puzzle_solver/__init__.py +8 -1
- puzzle_solver/core/utils.py +0 -153
- puzzle_solver/core/utils_visualizer.py +523 -0
- puzzle_solver/puzzles/binairo/binairo.py +44 -16
- puzzle_solver/puzzles/binairo/binairo_plus.py +7 -0
- puzzle_solver/puzzles/heyawake/heyawake.py +94 -0
- puzzle_solver/puzzles/kakuro/kakuro.py +77 -0
- puzzle_solver/puzzles/nurikabe/nurikabe.py +126 -0
- puzzle_solver/puzzles/palisade/palisade.py +2 -1
- puzzle_solver/puzzles/rectangles/rectangles.py +2 -2
- puzzle_solver/puzzles/shakashaka/shakashaka.py +201 -0
- puzzle_solver/puzzles/shingoki/shingoki.py +158 -0
- puzzle_solver/puzzles/singles/singles.py +14 -40
- puzzle_solver/puzzles/slitherlink/slitherlink.py +2 -1
- puzzle_solver/puzzles/stitches/parse_map/parse_map.py +3 -1
- puzzle_solver/puzzles/tapa/tapa.py +98 -0
- {multi_puzzle_solver-0.9.26.dist-info → multi_puzzle_solver-0.9.30.dist-info}/WHEEL +0 -0
- {multi_puzzle_solver-0.9.26.dist-info → multi_puzzle_solver-0.9.30.dist-info}/top_level.txt +0 -0
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
puzzle_solver/__init__.py,sha256=
|
|
2
|
-
puzzle_solver/core/utils.py,sha256=
|
|
1
|
+
puzzle_solver/__init__.py,sha256=f79JI0EQhfQi12yO6gvuzzLtxXQgojVrq-v8HZrzjS0,3693
|
|
2
|
+
puzzle_solver/core/utils.py,sha256=XBW5j-IwtJMPMP-ycmY6SqRCM1NOVl5O6UeoGqNj618,8153
|
|
3
3
|
puzzle_solver/core/utils_ortools.py,sha256=_i8cixHOB5XGqqcr-493bOiZgYJidnvxQMEfj--Trns,10278
|
|
4
|
+
puzzle_solver/core/utils_visualizer.py,sha256=2jBnS2PeI4keFf-rneScSxX669zXu5F1lkClZ5EkMhE,21152
|
|
4
5
|
puzzle_solver/puzzles/aquarium/aquarium.py,sha256=BUfkAS2d9eG3TdMoe1cOGGeNYgKUebRvn-z9nsC9gvE,5708
|
|
5
6
|
puzzle_solver/puzzles/battleships/battleships.py,sha256=RuYCrs4j0vUjlU139NRYYP-uNPAgO0V7hAzbsHrRwD8,7446
|
|
6
|
-
puzzle_solver/puzzles/binairo/binairo.py,sha256=
|
|
7
|
+
puzzle_solver/puzzles/binairo/binairo.py,sha256=4xgYd1ewYIQCqEzsHdgp6hWzyW_TF_2rt6PO8QLFKWU,6838
|
|
8
|
+
puzzle_solver/puzzles/binairo/binairo_plus.py,sha256=TvLG3olwANtft3LuCF-y4OofpU9PNa4IXDqgZqsD-g0,267
|
|
7
9
|
puzzle_solver/puzzles/black_box/black_box.py,sha256=ZnHDVt6PFS_r1kMNSsbz9hav1hxIrNDUvPyERGPjLjM,15635
|
|
8
10
|
puzzle_solver/puzzles/bridges/bridges.py,sha256=15A9uV4xjoqPRo_9CTnoKeGRxS3z2aMF619T1n0dTOQ,5402
|
|
9
11
|
puzzle_solver/puzzles/chess_range/chess_melee.py,sha256=D-_Oi8OyxsVe1j3dIKYwRlxgeb3NWLmDWGcv-oclY0c,195
|
|
@@ -16,10 +18,12 @@ puzzle_solver/puzzles/flip/flip.py,sha256=ZngJLUhRNc7qqo2wtNLdMPx4u9w9JTUge27Pmd
|
|
|
16
18
|
puzzle_solver/puzzles/galaxies/galaxies.py,sha256=p10lpmW0FjtneFCMEjG1FSiEpQuvD8zZG9FG8zYGoes,5582
|
|
17
19
|
puzzle_solver/puzzles/galaxies/parse_map/parse_map.py,sha256=v5TCrdREeOB69s9_QFgPHKA7flG69Im1HVzIdxH0qQc,9355
|
|
18
20
|
puzzle_solver/puzzles/guess/guess.py,sha256=sH-NlYhxM3DNbhk4eGde09kgM0KaDvSbLrpHQiwcFGo,10791
|
|
21
|
+
puzzle_solver/puzzles/heyawake/heyawake.py,sha256=qMnc_CuHn8K5Rw40tefjueI1pycpHQ7eN1R9Xg5WEuw,5601
|
|
19
22
|
puzzle_solver/puzzles/inertia/inertia.py,sha256=gJBahkh69CrSWNscalKEoP1j4X-Q3XpbIBMiG9PUpU0,5657
|
|
20
23
|
puzzle_solver/puzzles/inertia/tsp.py,sha256=gobiISHtARA4Elq0jr90p6Yhq11ULjGoqsS-rLFhYcc,15389
|
|
21
24
|
puzzle_solver/puzzles/inertia/parse_map/parse_map.py,sha256=A9JQTNqamUdzlwqks0XQp3Hge3mzyTIVK6YtDJvqpL4,8422
|
|
22
25
|
puzzle_solver/puzzles/kakurasu/kakurasu.py,sha256=VNGMJnBHDi6WkghLObRLhUvkmrPaGphTTUDMC0TkQvQ,2064
|
|
26
|
+
puzzle_solver/puzzles/kakuro/kakuro.py,sha256=Jf0Iilv32EPcaWikX92_vgBOVRp5MAE27aFRmnLotGQ,4374
|
|
23
27
|
puzzle_solver/puzzles/keen/keen.py,sha256=tDb6C5S3Q0JAKPsdw-84WQ6PxRADELZHr_BK8FDH-NA,5039
|
|
24
28
|
puzzle_solver/puzzles/light_up/light_up.py,sha256=iSA1rjZMFsnI0V0Nxivxox4qZkB7PvUrROSHXcoUXds,4541
|
|
25
29
|
puzzle_solver/puzzles/lits/lits.py,sha256=3fPIkhAIUz8JokcfaE_ZM3b0AFEnf5xPzGJ2qnm8SWY,7099
|
|
@@ -29,20 +33,24 @@ puzzle_solver/puzzles/minesweeper/minesweeper.py,sha256=LiQVOGkWCsc1WtX8CdPgL_Ww
|
|
|
29
33
|
puzzle_solver/puzzles/mosaic/mosaic.py,sha256=QX_nVpVKQg8OfaUcqFk9tKqsDyVqvZc6-XWvfI3YcSw,2175
|
|
30
34
|
puzzle_solver/puzzles/nonograms/nonograms.py,sha256=1jmDTOCnmivmBlwtMDyyk3TVqH5IjapzLn7zLQ4qubk,6056
|
|
31
35
|
puzzle_solver/puzzles/norinori/norinori.py,sha256=uC8vXAw35xsTmpmTeKqYW7tbcssms9LCcXFBONtV2Ng,4743
|
|
32
|
-
puzzle_solver/puzzles/
|
|
36
|
+
puzzle_solver/puzzles/nurikabe/nurikabe.py,sha256=VMJjB9KAKmfBkG1mDT3Jf2I1PZJb--Qx0BicN8xL4eg,6519
|
|
37
|
+
puzzle_solver/puzzles/palisade/palisade.py,sha256=T-LXlaLU5OwUQ24QWJWhBUFUktg0qDODTilNmBaXs4I,5014
|
|
33
38
|
puzzle_solver/puzzles/pearl/pearl.py,sha256=OhzpMYpxqvR3GCd5NH4ETT0NO4X753kRi6p5omYLChM,6798
|
|
34
39
|
puzzle_solver/puzzles/range/range.py,sha256=rruvD5ZSaOgvQuX6uGV_Dkr82nSiWZ5kDz03_j7Tt24,4425
|
|
35
|
-
puzzle_solver/puzzles/rectangles/rectangles.py,sha256=
|
|
40
|
+
puzzle_solver/puzzles/rectangles/rectangles.py,sha256=zaPg3qI9TNxr2iXmNi2kOL8R2RsS9DyQPUTY3ukgYIA,7033
|
|
41
|
+
puzzle_solver/puzzles/shakashaka/shakashaka.py,sha256=PRpg_qI7XA3ysAo_g1TRJsT3VwB5Vial2UcFyBOMwKQ,9571
|
|
42
|
+
puzzle_solver/puzzles/shingoki/shingoki.py,sha256=uwX1ZIGGDlshMtsZedlgGYE8hDB1ou3h6aBnZEr_l8I,7425
|
|
36
43
|
puzzle_solver/puzzles/signpost/signpost.py,sha256=-0_S6ycwzwlUf9-ZhP127Rgo5gMBOHiTM6t08dLLDac,3869
|
|
37
|
-
puzzle_solver/puzzles/singles/singles.py,sha256=
|
|
44
|
+
puzzle_solver/puzzles/singles/singles.py,sha256=KKn_Yl-eW874Bl1UmmcqoQ5vhNiO1JbM7fxKczOV5M4,2847
|
|
38
45
|
puzzle_solver/puzzles/slant/slant.py,sha256=xF-N4PuXYfx638NP1f1mi6YncIZB4mLtXtdS79XyPbg,6122
|
|
39
46
|
puzzle_solver/puzzles/slant/parse_map/parse_map.py,sha256=dxnALSDXe9wU0uSD0QEXnzoh1q801mj1ePTNLtG0n60,4796
|
|
40
|
-
puzzle_solver/puzzles/slitherlink/slitherlink.py,sha256=
|
|
47
|
+
puzzle_solver/puzzles/slitherlink/slitherlink.py,sha256=e1A_f_3J-QXN9fmt_Nf3FsYnp-TmE9TRKN06Wn4NnAU,7056
|
|
41
48
|
puzzle_solver/puzzles/star_battle/star_battle.py,sha256=IX6w4H3sifN01kPPtrAVRCK0Nl_xlXXSHvJKw8K1EuE,3718
|
|
42
49
|
puzzle_solver/puzzles/star_battle/star_battle_shapeless.py,sha256=lj05V0Y3A3NjMo1boMkPIwBhMtm6SWydjgAMeCf5EIo,225
|
|
43
50
|
puzzle_solver/puzzles/stitches/stitches.py,sha256=iK8t02q43gH3FPbuIDn4dK0sbaOgZOnw8yHNRNvNuIU,6534
|
|
44
|
-
puzzle_solver/puzzles/stitches/parse_map/parse_map.py,sha256=
|
|
51
|
+
puzzle_solver/puzzles/stitches/parse_map/parse_map.py,sha256=f49ZGVBPXjAGgqZnqPab6PcO_DsFDFZnG3uA8b-1d7k,10441
|
|
45
52
|
puzzle_solver/puzzles/sudoku/sudoku.py,sha256=SE4TM_gic6Jj0fkDR_NzUJdX2XKyQ8eeOnVAQ011Xbo,8870
|
|
53
|
+
puzzle_solver/puzzles/tapa/tapa.py,sha256=TsOQhnEvlC1JxaWiEjQg2KxRXJR49GrN71DsMvPpia8,5337
|
|
46
54
|
puzzle_solver/puzzles/tents/tents.py,sha256=iyVK2WXfIT5j_9qqlQg0WmwvixwXlZSsHGK3XA-KpII,6283
|
|
47
55
|
puzzle_solver/puzzles/thermometers/thermometers.py,sha256=nsvJZkm7G8FALT27bpaB0lv5E_AWawqmvapQI8QcYXw,4015
|
|
48
56
|
puzzle_solver/puzzles/towers/towers.py,sha256=QvL0Pp-Z2ewCeq9ZkNrh8MShKOh-Y52sFBSudve68wk,6496
|
|
@@ -53,7 +61,7 @@ puzzle_solver/puzzles/unruly/unruly.py,sha256=sDF0oKT50G-NshyW2DYrvAgD9q9Ku9ANUy
|
|
|
53
61
|
puzzle_solver/puzzles/yin_yang/yin_yang.py,sha256=WrRdNhmKhIARdGOt_36gpRxRzrfLGv3wl7igBpPFM64,5259
|
|
54
62
|
puzzle_solver/puzzles/yin_yang/parse_map/parse_map.py,sha256=drjfoHqmFf6U-ZQUwrBbfGINRxDQpgbvy4U3D9QyMhM,6617
|
|
55
63
|
puzzle_solver/utils/visualizer.py,sha256=tsX1yEKwmwXBYuBJpx_oZGe2UUt1g5yV73G3UbtmvtE,6817
|
|
56
|
-
multi_puzzle_solver-0.9.
|
|
57
|
-
multi_puzzle_solver-0.9.
|
|
58
|
-
multi_puzzle_solver-0.9.
|
|
59
|
-
multi_puzzle_solver-0.9.
|
|
64
|
+
multi_puzzle_solver-0.9.30.dist-info/METADATA,sha256=yxPV6ZvkvGPOs1O2HpIob3e94uFQXjpm5JJKdCXyc2s,335384
|
|
65
|
+
multi_puzzle_solver-0.9.30.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
66
|
+
multi_puzzle_solver-0.9.30.dist-info/top_level.txt,sha256=exwVUQa-anK9vYrpKzBPvH8bX43iElWI4VeNiAyBGJY,14
|
|
67
|
+
multi_puzzle_solver-0.9.30.dist-info/RECORD,,
|
puzzle_solver/__init__.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from puzzle_solver.puzzles.aquarium import aquarium as aquarium_solver
|
|
2
2
|
from puzzle_solver.puzzles.battleships import battleships as battleships_solver
|
|
3
3
|
from puzzle_solver.puzzles.binairo import binairo as binairo_solver
|
|
4
|
+
from puzzle_solver.puzzles.binairo import binairo_plus as binairo_plus_solver
|
|
4
5
|
from puzzle_solver.puzzles.black_box import black_box as black_box_solver
|
|
5
6
|
from puzzle_solver.puzzles.bridges import bridges as bridges_solver
|
|
6
7
|
from puzzle_solver.puzzles.chess_range import chess_range as chess_range_solver
|
|
@@ -11,8 +12,10 @@ from puzzle_solver.puzzles.filling import filling as filling_solver
|
|
|
11
12
|
from puzzle_solver.puzzles.flip import flip as flip_solver
|
|
12
13
|
from puzzle_solver.puzzles.galaxies import galaxies as galaxies_solver
|
|
13
14
|
from puzzle_solver.puzzles.guess import guess as guess_solver
|
|
15
|
+
from puzzle_solver.puzzles.heyawake import heyawake as heyawake_solver
|
|
14
16
|
from puzzle_solver.puzzles.inertia import inertia as inertia_solver
|
|
15
17
|
from puzzle_solver.puzzles.kakurasu import kakurasu as kakurasu_solver
|
|
18
|
+
from puzzle_solver.puzzles.kakuro import kakuro as kakuro_solver
|
|
16
19
|
from puzzle_solver.puzzles.keen import keen as keen_solver
|
|
17
20
|
from puzzle_solver.puzzles.light_up import light_up as light_up_solver
|
|
18
21
|
from puzzle_solver.puzzles.magnets import magnets as magnets_solver
|
|
@@ -21,11 +24,14 @@ from puzzle_solver.puzzles.minesweeper import minesweeper as minesweeper_solver
|
|
|
21
24
|
from puzzle_solver.puzzles.mosaic import mosaic as mosaic_solver
|
|
22
25
|
from puzzle_solver.puzzles.nonograms import nonograms as nonograms_solver
|
|
23
26
|
from puzzle_solver.puzzles.norinori import norinori as norinori_solver
|
|
27
|
+
from puzzle_solver.puzzles.nurikabe import nurikabe as nurikabe_solver
|
|
24
28
|
from puzzle_solver.puzzles.palisade import palisade as palisade_solver
|
|
25
29
|
from puzzle_solver.puzzles.lits import lits as lits_solver
|
|
26
30
|
from puzzle_solver.puzzles.pearl import pearl as pearl_solver
|
|
27
31
|
from puzzle_solver.puzzles.range import range as range_solver
|
|
28
32
|
from puzzle_solver.puzzles.rectangles import rectangles as rectangles_solver
|
|
33
|
+
from puzzle_solver.puzzles.shakashaka import shakashaka as shakashaka_solver
|
|
34
|
+
from puzzle_solver.puzzles.shingoki import shingoki as shingoki_solver
|
|
29
35
|
from puzzle_solver.puzzles.signpost import signpost as signpost_solver
|
|
30
36
|
from puzzle_solver.puzzles.singles import singles as singles_solver
|
|
31
37
|
from puzzle_solver.puzzles.slant import slant as slant_solver
|
|
@@ -34,6 +40,7 @@ from puzzle_solver.puzzles.star_battle import star_battle as star_battle_solver
|
|
|
34
40
|
from puzzle_solver.puzzles.star_battle import star_battle_shapeless as star_battle_shapeless_solver
|
|
35
41
|
from puzzle_solver.puzzles.stitches import stitches as stitches_solver
|
|
36
42
|
from puzzle_solver.puzzles.sudoku import sudoku as sudoku_solver
|
|
43
|
+
from puzzle_solver.puzzles.tapa import tapa as tapa_solver
|
|
37
44
|
from puzzle_solver.puzzles.tents import tents as tents_solver
|
|
38
45
|
from puzzle_solver.puzzles.thermometers import thermometers as thermometers_solver
|
|
39
46
|
from puzzle_solver.puzzles.towers import towers as towers_solver
|
|
@@ -45,4 +52,4 @@ from puzzle_solver.puzzles.yin_yang import yin_yang as yin_yang_solver
|
|
|
45
52
|
|
|
46
53
|
from puzzle_solver.puzzles.inertia.parse_map.parse_map import main as inertia_image_parser
|
|
47
54
|
|
|
48
|
-
__version__ = '0.9.
|
|
55
|
+
__version__ = '0.9.30'
|
puzzle_solver/core/utils.py
CHANGED
|
@@ -230,156 +230,3 @@ def polyominoes_with_shape_id(N):
|
|
|
230
230
|
result = {(frozenset(Pos(x, y) for x, y in s), _id) for s, _id in result}
|
|
231
231
|
return result
|
|
232
232
|
|
|
233
|
-
|
|
234
|
-
def render_grid(cell_flags: np.ndarray,
|
|
235
|
-
center_char: Union[np.ndarray, str, None] = None,
|
|
236
|
-
show_axes: bool = True,
|
|
237
|
-
scale_x: int = 2) -> str:
|
|
238
|
-
"""
|
|
239
|
-
most of this function was AI generated then modified by me, I don't currently care about the details of rendering to the terminal this looked good enough during my testing.
|
|
240
|
-
cell_flags: np.ndarray of shape (N, N) with characters 'U', 'D', 'L', 'R' to represent the edges of the cells.
|
|
241
|
-
center_char: np.ndarray of shape (N, N) with the center of the cells, or a string to use for all cells, or None to not show centers.
|
|
242
|
-
scale_x: horizontal stretch factor (>=1). Try 2 or 3 for squarer cells.
|
|
243
|
-
"""
|
|
244
|
-
assert cell_flags is not None and cell_flags.ndim == 2
|
|
245
|
-
R, C = cell_flags.shape
|
|
246
|
-
|
|
247
|
-
# Edge presence arrays (note the rectangular shapes)
|
|
248
|
-
H = np.zeros((R+1, C), dtype=bool) # horizontal edges between rows
|
|
249
|
-
V = np.zeros((R, C+1), dtype=bool) # vertical edges between cols
|
|
250
|
-
for r in range(R):
|
|
251
|
-
for c in range(C):
|
|
252
|
-
s = cell_flags[r, c]
|
|
253
|
-
if 'U' in s: H[r, c] = True
|
|
254
|
-
if 'D' in s: H[r+1, c] = True
|
|
255
|
-
if 'L' in s: V[r, c] = True
|
|
256
|
-
if 'R' in s: V[r, c+1] = True
|
|
257
|
-
|
|
258
|
-
# Bitmask for corner connections
|
|
259
|
-
U, Rb, D, Lb = 1, 2, 4, 8
|
|
260
|
-
JUNCTION = {
|
|
261
|
-
0: ' ',
|
|
262
|
-
U: '│', D: '│', U|D: '│',
|
|
263
|
-
Lb: '─', Rb: '─', Lb|Rb: '─',
|
|
264
|
-
U|Rb: '└', Rb|D: '┌', D|Lb: '┐', Lb|U: '┘',
|
|
265
|
-
U|D|Lb: '┤', U|D|Rb: '├', Lb|Rb|U: '┴', Lb|Rb|D: '┬',
|
|
266
|
-
U|Rb|D|Lb: '┼',
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
assert scale_x >= 1
|
|
270
|
-
assert H.shape == (R+1, C) and V.shape == (R, C+1)
|
|
271
|
-
|
|
272
|
-
rows = 2*R + 1
|
|
273
|
-
cols = 2*C*scale_x + 1
|
|
274
|
-
canvas = [[' ']*cols for _ in range(rows)]
|
|
275
|
-
|
|
276
|
-
def x_corner(c): # x of corner column c (0..C)
|
|
277
|
-
return (2*c) * scale_x
|
|
278
|
-
def x_between(c,k): # kth in-between col (1..2*scale_x-1) between corners c and c+1
|
|
279
|
-
return (2*c) * scale_x + k
|
|
280
|
-
|
|
281
|
-
# horizontal edges: fill the stretched band between corners with '─'
|
|
282
|
-
for r in range(R+1):
|
|
283
|
-
rr = 2*r
|
|
284
|
-
for c in range(C):
|
|
285
|
-
if H[r, c]:
|
|
286
|
-
for k in range(1, scale_x*2): # 1..(2*scale_x-1)
|
|
287
|
-
canvas[rr][x_between(c, k)] = '─'
|
|
288
|
-
|
|
289
|
-
# vertical edges: at the corner columns
|
|
290
|
-
for r in range(R):
|
|
291
|
-
rr = 2*r + 1
|
|
292
|
-
for c in range(C+1):
|
|
293
|
-
if V[r, c]:
|
|
294
|
-
canvas[rr][x_corner(c)] = '│'
|
|
295
|
-
|
|
296
|
-
# junctions at every corner grid point
|
|
297
|
-
for r in range(R+1):
|
|
298
|
-
rr = 2*r
|
|
299
|
-
for c in range(C+1):
|
|
300
|
-
m = 0
|
|
301
|
-
if r > 0 and V[r-1, c]: m |= U
|
|
302
|
-
if c < C and H[r, c]: m |= Rb
|
|
303
|
-
if r < R and V[r, c]: m |= D
|
|
304
|
-
if c > 0 and H[r, c-1]: m |= Lb
|
|
305
|
-
canvas[rr][x_corner(c)] = JUNCTION[m]
|
|
306
|
-
|
|
307
|
-
# centers (safe for multi-character strings)
|
|
308
|
-
def put_center_text(rr: int, c: int, text: str):
|
|
309
|
-
left = x_corner(c) + 1
|
|
310
|
-
right = x_corner(c+1) - 1
|
|
311
|
-
if right < left:
|
|
312
|
-
return
|
|
313
|
-
span_width = right - left + 1
|
|
314
|
-
s = str(text)
|
|
315
|
-
if len(s) > span_width:
|
|
316
|
-
s = s[:span_width] # truncate to protect borders
|
|
317
|
-
start = left + (span_width - len(s)) // 2
|
|
318
|
-
for i, ch in enumerate(s):
|
|
319
|
-
canvas[rr][start + i] = ch
|
|
320
|
-
|
|
321
|
-
if center_char is not None:
|
|
322
|
-
for r in range(R):
|
|
323
|
-
rr = 2*r + 1
|
|
324
|
-
for c in range(C):
|
|
325
|
-
val = center_char if isinstance(center_char, str) else center_char[r, c]
|
|
326
|
-
put_center_text(rr, c, '' if val is None else str(val))
|
|
327
|
-
|
|
328
|
-
# rows -> strings
|
|
329
|
-
art_rows = [''.join(row) for row in canvas]
|
|
330
|
-
if not show_axes:
|
|
331
|
-
return '\n'.join(art_rows)
|
|
332
|
-
|
|
333
|
-
# Axes labels: row indices on the left, column indices on top (handle C, not R)
|
|
334
|
-
gut = max(2, len(str(R-1))) # gutter width based on row index width
|
|
335
|
-
gutter = ' ' * gut
|
|
336
|
-
top_tens = list(gutter + ' ' * cols)
|
|
337
|
-
top_ones = list(gutter + ' ' * cols)
|
|
338
|
-
|
|
339
|
-
for c in range(C):
|
|
340
|
-
xc_center = x_corner(c) + scale_x
|
|
341
|
-
if C >= 10:
|
|
342
|
-
top_tens[gut + xc_center] = str((c // 10) % 10)
|
|
343
|
-
top_ones[gut + xc_center] = str(c % 10)
|
|
344
|
-
|
|
345
|
-
if gut >= 2:
|
|
346
|
-
top_tens[gut-2:gut] = list(' ')
|
|
347
|
-
top_ones[gut-2:gut] = list(' ')
|
|
348
|
-
|
|
349
|
-
labeled = []
|
|
350
|
-
for r, line in enumerate(art_rows):
|
|
351
|
-
if r % 2 == 1: # cell-center row
|
|
352
|
-
label = str(r//2).rjust(gut)
|
|
353
|
-
else:
|
|
354
|
-
label = ' ' * gut
|
|
355
|
-
labeled.append(label + line)
|
|
356
|
-
|
|
357
|
-
return ''.join(top_tens) + '\n' + ''.join(top_ones) + '\n' + '\n'.join(labeled)
|
|
358
|
-
|
|
359
|
-
def id_board_to_wall_board(id_board: np.array, border_is_wall = True) -> np.array:
|
|
360
|
-
"""In many instances, we have a 2d array where cell values are arbitrary ids
|
|
361
|
-
and we want to convert it to a 2d array where cell values are walls "U", "D", "L", "R" to represent the edges that separate me from my neighbors that have different ids.
|
|
362
|
-
Args:
|
|
363
|
-
id_board: np.array of shape (N, N) with arbitrary ids.
|
|
364
|
-
border_is_wall: if True, the edges of the board are considered to be walls.
|
|
365
|
-
Returns:
|
|
366
|
-
np.array of shape (N, N) with walls "U", "D", "L", "R".
|
|
367
|
-
"""
|
|
368
|
-
res = np.full((id_board.shape[0], id_board.shape[1]), '', dtype=object)
|
|
369
|
-
V, H = id_board.shape
|
|
370
|
-
def append_char(pos: Pos, s: str):
|
|
371
|
-
set_char(res, pos, get_char(res, pos) + s)
|
|
372
|
-
def handle_pos_direction(pos: Pos, direction: Direction, s: str):
|
|
373
|
-
pos2 = get_next_pos(pos, direction)
|
|
374
|
-
if in_bounds(pos2, V, H):
|
|
375
|
-
if get_char(id_board, pos2) != get_char(id_board, pos):
|
|
376
|
-
append_char(pos, s)
|
|
377
|
-
else:
|
|
378
|
-
if border_is_wall:
|
|
379
|
-
append_char(pos, s)
|
|
380
|
-
for pos in get_all_pos(V, H):
|
|
381
|
-
handle_pos_direction(pos, Direction.LEFT, 'L')
|
|
382
|
-
handle_pos_direction(pos, Direction.RIGHT, 'R')
|
|
383
|
-
handle_pos_direction(pos, Direction.UP, 'U')
|
|
384
|
-
handle_pos_direction(pos, Direction.DOWN, 'D')
|
|
385
|
-
return res
|