puzzlekit 0.3.2__tar.gz → 0.3.3__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.
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/PKG-INFO +62 -4
- puzzlekit-0.3.2/src/puzzlekit.egg-info/PKG-INFO → puzzlekit-0.3.3/README.md +59 -52
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/pyproject.toml +3 -3
- puzzlekit-0.3.3/src/puzzlekit/__init__.py +224 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/base.py +250 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/debug.py +570 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/penpa_converter.py +478 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/penpa_template.py +188 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/puzzle_types.py +175 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/puzzlink_converter.py +1690 -0
- puzzlekit-0.3.3/src/puzzlekit/formats/utils.py +194 -0
- puzzlekit-0.3.2/README.md → puzzlekit-0.3.3/src/puzzlekit.egg-info/PKG-INFO +110 -1
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit.egg-info/SOURCES.txt +15 -2
- puzzlekit-0.3.3/tests/__init__.py +0 -0
- puzzlekit-0.3.3/tests/__pycache__/__init__.cpython-310.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/metadata_test_utils.cpython-310.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_abc_end_view.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_akari.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_balance_loop.cpython-310-pytest-9.0.2.pyc +0 -0
- puzzlekit-0.3.3/tests/__pycache__/test_battleship.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_binairo.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_bosanowa.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_bricks.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_buraitoraito.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_butterfly_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_castle_wall.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_cave.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_clueless_1_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_clueless_2_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_country_road.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_creek.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_detour.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_diff_neighbors.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_dominos.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_dotchi_loop.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_double_back.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_entry_exit.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_eulero.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_even_odd_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_fobidoshi.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_fuzuli.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_gappy.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_gattai_8_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_grand_tour.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_grid.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_hakoiri.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_hakyuu.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_heyawake.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_hidoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_hitori.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_jigsaw_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_juosan.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_kakkuru.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_kakurasu.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_kakuro.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_ken_ken.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_killer_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_koburin.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_kuromasu.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_kuroshuto.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_linesweeper.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_lits.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_magnetic.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_makaro.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_masyu.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_mathrax.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_minesweeper.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_moon_sun.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_mosaic.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_munraito.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_nawabari.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_nondango.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_nonogram.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_norinori.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_number_cross.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_one_to_x.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_paint_area.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_patchwork.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_pfeilzahlen.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_pills.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_putteria.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_regional_yajilin.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_renban.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_samurai_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_shikaku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_shingoki.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_shogun_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_simple_loop.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_skyscraper.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_slitherlink.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_snake.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_sohei_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_square_o.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_starbattle.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_stitches.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_str8t.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_suguru.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_sumo_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_tenner_grid.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_tent.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_terra_x.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_thermometer.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_tile_paint.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_trinairo.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_windmill_sudoku.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_yajikabe.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_yajilin.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/__pycache__/test_yin_yang.cpython-310-pytest-9.0.2.pyc +0 -0
- puzzlekit-0.3.3/tests/formats/__pycache__/conftest.cpython-310-pytest-9.0.2.pyc +0 -0
- puzzlekit-0.3.3/tests/formats/__pycache__/test_cross_format.cpython-310-pytest-9.0.2.pyc +0 -0
- puzzlekit-0.3.3/tests/formats/__pycache__/test_roundtrip.cpython-310-pytest-9.0.2.pyc +0 -0
- puzzlekit-0.3.3/tests/formats/conftest.py +173 -0
- puzzlekit-0.3.3/tests/formats/test_cross_format.py +23 -0
- puzzlekit-0.3.3/tests/formats/test_roundtrip.py +96 -0
- puzzlekit-0.3.2/src/puzzlekit/__init__.py +0 -75
- puzzlekit-0.3.2/src/puzzlekit/core/puzzlink.py +0 -536
- puzzlekit-0.3.2/tests/__pycache__/__init__.cpython-310.pyc +0 -0
- puzzlekit-0.3.2/tests/__pycache__/test_battleship.cpython-310-pytest-9.0.2.pyc +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/LICENSE +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/MANIFEST.in +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/setup.cfg +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/direction.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/docs_template.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/grid.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/position.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/regionsgrid.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/result.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/core/solver.py +0 -0
- {puzzlekit-0.3.2/src/puzzlekit/utils → puzzlekit-0.3.3/src/puzzlekit/formats}/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/parsers/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/parsers/common.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/parsers/registry.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/abc_end_view.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/akari.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/aqre.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/balance_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/battleship.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/binairo.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/bosanowa.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/bricks.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/buraitoraito.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/butterfly_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/canal_view.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/castle_wall.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/cave.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/clueless_1_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/clueless_2_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/cojun.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/country_road.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/creek.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/detour.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/diff_neighbors.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/dominos.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/dotchi_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/double_back.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/entry_exit.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/eulero.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/even_odd_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/fobidoshi.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/fuzuli.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/gappy.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/gattai_8_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/geradeweg.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/grand_tour.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/hakoiri.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/hakyuu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/heyawake.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/hidoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/hitori.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/jigsaw_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/juosan.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kakkuru.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kakurasu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kakuro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/ken_ken.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/killer_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/koburin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kuromasu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kuroshuto.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/kurotto.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/linesweeper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/lits.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/magnetic.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/makaro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/masyu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/mathrax.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/mejilink.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/mid_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/minesweeper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/moon_sun.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/mosaic.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/munraito.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nanro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nawabari.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nondango.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nonogram.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/norinori.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/number_cross.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nurikabe.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/nurimisaki.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/one_to_x.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/paint_area.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/patchwork.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/pfeilzahlen.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/pills.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/pipes.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/putteria.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/regional_yajilin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/renban.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/samurai_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shikaku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shimaguni.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shingoki.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shirokuro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shogun_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/shugaku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/simple_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/skyscraper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/slitherlink.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/slitherlink_duality.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/snake.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/sohei_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/square_o.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/starbattle.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/stitches.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/str8t.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/suguru.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/sumo_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/tatamibari.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/tenner_grid.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/tent.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/terra_x.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/thermometer.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/tile_paint.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/trinairo.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/usoone.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/windmill_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/yajikabe.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/yajilin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/solvers/yin_yang.py +0 -0
- {puzzlekit-0.3.2/tests → puzzlekit-0.3.3/src/puzzlekit/utils}/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/utils/file_loader.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/utils/name_utils.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/utils/ortools_utils.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/utils/puzzle_math.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/verifiers/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/verifiers/common.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/viz/__init__.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/viz/base.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit/viz/drawers.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit.egg-info/dependency_links.txt +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit.egg-info/requires.txt +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/src/puzzlekit.egg-info/top_level.txt +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/metadata_test_utils.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_abc_end_view.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_akari.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_balance_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_battleship.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_binairo.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_bosanowa.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_bricks.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_buraitoraito.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_butterfly_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_castle_wall.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_cave.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_clueless_1_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_clueless_2_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_country_road.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_creek.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_detour.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_diff_neighbors.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_dominos.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_dotchi_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_double_back.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_entry_exit.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_eulero.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_even_odd_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_fobidoshi.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_fuzuli.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_gappy.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_gattai_8_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_grand_tour.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_grid.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_hakoiri.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_hakyuu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_heyawake.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_hidoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_hitori.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_jigsaw_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_juosan.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_kakkuru.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_kakurasu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_kakuro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_ken_ken.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_killer_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_koburin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_kuromasu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_kuroshuto.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_linesweeper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_lits.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_magnetic.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_makaro.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_masyu.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_mathrax.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_minesweeper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_moon_sun.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_mosaic.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_munraito.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_nawabari.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_nondango.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_nonogram.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_norinori.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_number_cross.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_one_to_x.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_paint_area.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_patchwork.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_pfeilzahlen.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_pills.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_putteria.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_regional_yajilin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_renban.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_samurai_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_shikaku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_shingoki.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_shogun_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_simple_loop.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_skyscraper.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_slitherlink.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_snake.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_sohei_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_square_o.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_starbattle.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_stitches.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_str8t.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_suguru.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_sumo_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_tenner_grid.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_tent.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_terra_x.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_thermometer.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_tile_paint.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_trinairo.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_windmill_sudoku.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_yajikabe.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_yajilin.py +0 -0
- {puzzlekit-0.3.2 → puzzlekit-0.3.3}/tests/test_yin_yang.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: puzzlekit
|
|
3
|
-
Version: 0.3.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.3.3
|
|
4
|
+
Summary: 100+ logic puzzle solvers on Google OR-Tools (e.g., yajilin, slitherlink, nonogram), with bidirectional conversion between puzz.link and Penpa+ URLs for supported genres (shared IR).
|
|
5
5
|
Author-email: SmilingWayne <xiaoxiaowayne@gmail.com>
|
|
6
6
|
License: MIT License
|
|
7
7
|
|
|
@@ -28,7 +28,7 @@ License: MIT License
|
|
|
28
28
|
Project-URL: Homepage, https://github.com/SmilingWayne/PuzzleSolver
|
|
29
29
|
Project-URL: Repository, https://github.com/SmilingWayne/PuzzleSolver
|
|
30
30
|
Project-URL: Issues, https://github.com/SmilingWayne/PuzzleSolver/issues
|
|
31
|
-
Keywords: puzzle,solver,logic,cp-sat,
|
|
31
|
+
Keywords: puzzle,solver,logic,cp-sat,python,nonogram,slitherlink,yajilin,sudoku,puzzlink,penpa,url-conversion
|
|
32
32
|
Classifier: Development Status :: 3 - Alpha
|
|
33
33
|
Classifier: Intended Audience :: Developers
|
|
34
34
|
Classifier: Intended Audience :: Science/Research
|
|
@@ -55,7 +55,7 @@ This repository provides **100+ useful, efficient and problem‑specific solvers
|
|
|
55
55
|
|
|
56
56
|
For simplicity, the dataset is removed to [puzzlekit-dataset](https://github.com/SmilingWayne/puzzlekit-dataset) repo. The structured dataset contains 41k+ instances covering 130+ specific and popular puzzle types (e.g. Nonogram, Slitherlink, Akari, Fillomino, Hitori, Kakuro, Kakuro), mostly from [Raetsel's Janko](https://www.janko.at/Raetsel/index.htm) and [puzz.link](https://puzz.link). The details are listed in the table below.
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
This repo also provides **bidirectional conversion between [puzz.link](https://puzz.link) and [Penpa+](https://swaroopg92.github.io/penpa-edit/) puzzle URLs** for supported genres (19 for now) via a shared intermediate representation. puzz.link-like links from common mirrors (e.g., `pzplus.tck.mn`, `pzv.jp`) are also accepted.
|
|
59
59
|
|
|
60
60
|
<details>
|
|
61
61
|
<summary><strong>Table of puzzles, datasets and solvers.</strong></summary>
|
|
@@ -215,6 +215,37 @@ For simplicity, the dataset is removed to [puzzlekit-dataset](https://github.com
|
|
|
215
215
|
|
|
216
216
|
</details>
|
|
217
217
|
|
|
218
|
+
<details>
|
|
219
|
+
<summary><strong>Supported puzzle types for URL interchange</strong>
|
|
220
|
+
</summary>
|
|
221
|
+
|
|
222
|
+
> (canonical IR name, puzz.link token, aliases, Penpa genre tag)
|
|
223
|
+
|
|
224
|
+
| Canonical (IR) | puzz.link token | puzz.link aliases | Penpa genre tag |
|
|
225
|
+
| -------------- | --------------- | ------------------------------- | --------------------- |
|
|
226
|
+
| `aqre` | `aqre` | aqre | `aqre` |
|
|
227
|
+
| `ayeheya` | `ayeheya` | ayeheya | `ayeheya (ekawayeh)` |
|
|
228
|
+
| `castle` | `castle` | castle | `castlewall` |
|
|
229
|
+
| `country` | `country` | country | `country road` |
|
|
230
|
+
| `hebi` | `hebi` | hebi, snakes | `hebi-ichigo` |
|
|
231
|
+
| `heyawake` | `heyawake` | heyawake, heyawacky, heyawack | `heyawake` |
|
|
232
|
+
| `kurochute` | `kurochute` | kurochute, kuroshuto, kurochuto | `kurochute` |
|
|
233
|
+
| `kurodoko` | `kurodoko` | kurodoko | `kurodoko` |
|
|
234
|
+
| `kurotto` | `kurotto` | kurotto | `kurotto` |
|
|
235
|
+
| `masyu` | `masyu` | masyu, mashu, pearl | `masyu` |
|
|
236
|
+
| `moonsun` | `moonsun` | moonsun | `moon or sun` |
|
|
237
|
+
| `nonogram` | `nonogram` | nonogram | `nonogram` |
|
|
238
|
+
| `nurikabe` | `nurikabe` | nurikabe | `nurikabe` |
|
|
239
|
+
| `nurimisaki` | `nurimisaki` | nurimisaki | `nurimisaki` |
|
|
240
|
+
| `shikaku` | `shikaku` | shikaku | `shikaku` |
|
|
241
|
+
| `shimaguni` | `shimaguni` | shimaguni | `shimaguni (islands)` |
|
|
242
|
+
| `slitherlink` | `slither` | slitherlink | `slitherlink` |
|
|
243
|
+
| `stostone` | `stostone` | stostone | `stostone` |
|
|
244
|
+
| `yajilin` | `yajilin` | yajilin, yajirin | `yajilin` |
|
|
245
|
+
|
|
246
|
+
</details>
|
|
247
|
+
|
|
248
|
+
|
|
218
249
|
|
|
219
250
|
<details>
|
|
220
251
|
<summary><strong>Gallery of some puzzles (not complete!)</strong></summary>
|
|
@@ -291,6 +322,33 @@ python scripts/benchmark.py -a
|
|
|
291
322
|
|
|
292
323
|
Currently it will take ~30 min to solve all 30k+ instances available.
|
|
293
324
|
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
If you want to try the bi-directional conversion between penpa+ and puzz.link, follow the quick tour:
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
```python
|
|
331
|
+
import puzzlekit
|
|
332
|
+
|
|
333
|
+
# 1st method
|
|
334
|
+
ir = puzzlekit.decode("https://puzz.link/p?slither/10/10/g188227cl1dg367bdcg3ddgbhdgd1agbd760dg2cl633661d")
|
|
335
|
+
penpa_url = puzzlekit.encode(ir, "penpa")
|
|
336
|
+
print(penpa_url)
|
|
337
|
+
# get:
|
|
338
|
+
# https://swaroopg92.github.io/penpa-edit/#m=edit&p=7VdtT9swEP7Or0...
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
# 2nd method (recommend)
|
|
342
|
+
penpa_url = "YOUR_PENPA_URL" # both full URL or `m=edit&p=...` are okay
|
|
343
|
+
puzzlink_url = puzzlekit.convert(penpa_url, "puzzlink")
|
|
344
|
+
print(puzzlink_url)
|
|
345
|
+
# get:
|
|
346
|
+
# https://puzz.link/p?slither/10/10/b86ag68dg127bg62aldg8dad8bgdl26dg722cg68dg88b3
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
See also `scripts/quick_start.py` for a runnable sample.
|
|
350
|
+
|
|
351
|
+
|
|
294
352
|
## Roadmap
|
|
295
353
|
|
|
296
354
|
- [x] 130+ Puzzle Solvers & 40k+ Dataset.
|
|
@@ -1,61 +1,10 @@
|
|
|
1
|
-
Metadata-Version: 2.4
|
|
2
|
-
Name: puzzlekit
|
|
3
|
-
Version: 0.3.2
|
|
4
|
-
Summary: A comprehensive logic puzzle solver (100+) based on Google OR-Tools. e.g., solvers for Nonogram, Slitherlink, Akari, Yajilin, Hitori and Sudoku-variants.
|
|
5
|
-
Author-email: SmilingWayne <xiaoxiaowayne@gmail.com>
|
|
6
|
-
License: MIT License
|
|
7
|
-
|
|
8
|
-
Copyright (c) 2025 SmilingWayne
|
|
9
|
-
|
|
10
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
-
in the Software without restriction, including without limitation the rights
|
|
13
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
-
furnished to do so, subject to the following conditions:
|
|
16
|
-
|
|
17
|
-
The above copyright notice and this permission notice shall be included in all
|
|
18
|
-
copies or substantial portions of the Software.
|
|
19
|
-
|
|
20
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
-
SOFTWARE.
|
|
27
|
-
|
|
28
|
-
Project-URL: Homepage, https://github.com/SmilingWayne/PuzzleSolver
|
|
29
|
-
Project-URL: Repository, https://github.com/SmilingWayne/PuzzleSolver
|
|
30
|
-
Project-URL: Issues, https://github.com/SmilingWayne/PuzzleSolver/issues
|
|
31
|
-
Keywords: puzzle,solver,logic,cp-sat,efficient,python,nonogram,slitherlink,yajilin,hitori,sudoku,akari
|
|
32
|
-
Classifier: Development Status :: 3 - Alpha
|
|
33
|
-
Classifier: Intended Audience :: Developers
|
|
34
|
-
Classifier: Intended Audience :: Science/Research
|
|
35
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
36
|
-
Classifier: Programming Language :: Python :: 3
|
|
37
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
38
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.12
|
|
40
|
-
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
41
|
-
Classifier: Topic :: Games/Entertainment :: Puzzle Games
|
|
42
|
-
Classifier: Operating System :: OS Independent
|
|
43
|
-
Requires-Python: >=3.10
|
|
44
|
-
Description-Content-Type: text/markdown
|
|
45
|
-
License-File: LICENSE
|
|
46
|
-
Requires-Dist: numpy
|
|
47
|
-
Requires-Dist: ortools>=9.6
|
|
48
|
-
Requires-Dist: matplotlib
|
|
49
|
-
Requires-Dist: typeguard
|
|
50
|
-
Dynamic: license-file
|
|
51
|
-
|
|
52
1
|
# Puzzle Kit
|
|
53
2
|
|
|
54
3
|
This repository provides **100+ useful, efficient and problem‑specific solvers** for a variety of **logic puzzles**. The underlying solving engine is the open‑source Google [OR-Tools](https://developers.google.cn/optimization) CP-SAT solver. For more details of puzzles and their input format, you can refer to [docs of puzzlekit](https://smilingwayne.github.io/PuzzleSolver/).
|
|
55
4
|
|
|
56
5
|
For simplicity, the dataset is removed to [puzzlekit-dataset](https://github.com/SmilingWayne/puzzlekit-dataset) repo. The structured dataset contains 41k+ instances covering 130+ specific and popular puzzle types (e.g. Nonogram, Slitherlink, Akari, Fillomino, Hitori, Kakuro, Kakuro), mostly from [Raetsel's Janko](https://www.janko.at/Raetsel/index.htm) and [puzz.link](https://puzz.link). The details are listed in the table below.
|
|
57
6
|
|
|
58
|
-
|
|
7
|
+
This repo also provides **bidirectional conversion between [puzz.link](https://puzz.link) and [Penpa+](https://swaroopg92.github.io/penpa-edit/) puzzle URLs** for supported genres (19 for now) via a shared intermediate representation. puzz.link-like links from common mirrors (e.g., `pzplus.tck.mn`, `pzv.jp`) are also accepted.
|
|
59
8
|
|
|
60
9
|
<details>
|
|
61
10
|
<summary><strong>Table of puzzles, datasets and solvers.</strong></summary>
|
|
@@ -215,6 +164,37 @@ For simplicity, the dataset is removed to [puzzlekit-dataset](https://github.com
|
|
|
215
164
|
|
|
216
165
|
</details>
|
|
217
166
|
|
|
167
|
+
<details>
|
|
168
|
+
<summary><strong>Supported puzzle types for URL interchange</strong>
|
|
169
|
+
</summary>
|
|
170
|
+
|
|
171
|
+
> (canonical IR name, puzz.link token, aliases, Penpa genre tag)
|
|
172
|
+
|
|
173
|
+
| Canonical (IR) | puzz.link token | puzz.link aliases | Penpa genre tag |
|
|
174
|
+
| -------------- | --------------- | ------------------------------- | --------------------- |
|
|
175
|
+
| `aqre` | `aqre` | aqre | `aqre` |
|
|
176
|
+
| `ayeheya` | `ayeheya` | ayeheya | `ayeheya (ekawayeh)` |
|
|
177
|
+
| `castle` | `castle` | castle | `castlewall` |
|
|
178
|
+
| `country` | `country` | country | `country road` |
|
|
179
|
+
| `hebi` | `hebi` | hebi, snakes | `hebi-ichigo` |
|
|
180
|
+
| `heyawake` | `heyawake` | heyawake, heyawacky, heyawack | `heyawake` |
|
|
181
|
+
| `kurochute` | `kurochute` | kurochute, kuroshuto, kurochuto | `kurochute` |
|
|
182
|
+
| `kurodoko` | `kurodoko` | kurodoko | `kurodoko` |
|
|
183
|
+
| `kurotto` | `kurotto` | kurotto | `kurotto` |
|
|
184
|
+
| `masyu` | `masyu` | masyu, mashu, pearl | `masyu` |
|
|
185
|
+
| `moonsun` | `moonsun` | moonsun | `moon or sun` |
|
|
186
|
+
| `nonogram` | `nonogram` | nonogram | `nonogram` |
|
|
187
|
+
| `nurikabe` | `nurikabe` | nurikabe | `nurikabe` |
|
|
188
|
+
| `nurimisaki` | `nurimisaki` | nurimisaki | `nurimisaki` |
|
|
189
|
+
| `shikaku` | `shikaku` | shikaku | `shikaku` |
|
|
190
|
+
| `shimaguni` | `shimaguni` | shimaguni | `shimaguni (islands)` |
|
|
191
|
+
| `slitherlink` | `slither` | slitherlink | `slitherlink` |
|
|
192
|
+
| `stostone` | `stostone` | stostone | `stostone` |
|
|
193
|
+
| `yajilin` | `yajilin` | yajilin, yajirin | `yajilin` |
|
|
194
|
+
|
|
195
|
+
</details>
|
|
196
|
+
|
|
197
|
+
|
|
218
198
|
|
|
219
199
|
<details>
|
|
220
200
|
<summary><strong>Gallery of some puzzles (not complete!)</strong></summary>
|
|
@@ -291,6 +271,33 @@ python scripts/benchmark.py -a
|
|
|
291
271
|
|
|
292
272
|
Currently it will take ~30 min to solve all 30k+ instances available.
|
|
293
273
|
|
|
274
|
+
---
|
|
275
|
+
|
|
276
|
+
If you want to try the bi-directional conversion between penpa+ and puzz.link, follow the quick tour:
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
```python
|
|
280
|
+
import puzzlekit
|
|
281
|
+
|
|
282
|
+
# 1st method
|
|
283
|
+
ir = puzzlekit.decode("https://puzz.link/p?slither/10/10/g188227cl1dg367bdcg3ddgbhdgd1agbd760dg2cl633661d")
|
|
284
|
+
penpa_url = puzzlekit.encode(ir, "penpa")
|
|
285
|
+
print(penpa_url)
|
|
286
|
+
# get:
|
|
287
|
+
# https://swaroopg92.github.io/penpa-edit/#m=edit&p=7VdtT9swEP7Or0...
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# 2nd method (recommend)
|
|
291
|
+
penpa_url = "YOUR_PENPA_URL" # both full URL or `m=edit&p=...` are okay
|
|
292
|
+
puzzlink_url = puzzlekit.convert(penpa_url, "puzzlink")
|
|
293
|
+
print(puzzlink_url)
|
|
294
|
+
# get:
|
|
295
|
+
# https://puzz.link/p?slither/10/10/b86ag68dg127bg62aldg8dad8bgdl26dg722cg68dg88b3
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
See also `scripts/quick_start.py` for a runnable sample.
|
|
299
|
+
|
|
300
|
+
|
|
294
301
|
## Roadmap
|
|
295
302
|
|
|
296
303
|
- [x] 130+ Puzzle Solvers & 40k+ Dataset.
|
|
@@ -4,13 +4,13 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "puzzlekit"
|
|
7
|
-
version = "0.3.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.3.3"
|
|
8
|
+
description = "100+ logic puzzle solvers on Google OR-Tools (e.g., yajilin, slitherlink, nonogram), with bidirectional conversion between puzz.link and Penpa+ URLs for supported genres (shared IR)."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
11
11
|
authors = [{name = "SmilingWayne", email = "xiaoxiaowayne@gmail.com"}]
|
|
12
12
|
license = {file = "LICENSE"}
|
|
13
|
-
keywords = ["puzzle", "solver", "logic", "cp-sat", "
|
|
13
|
+
keywords = ["puzzle", "solver", "logic", "cp-sat", "python", "nonogram", "slitherlink", "yajilin", "sudoku", "puzzlink", "penpa", "url-conversion"]
|
|
14
14
|
classifiers = [
|
|
15
15
|
"Development Status :: 3 - Alpha",
|
|
16
16
|
"Intended Audience :: Developers",
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
from typing import Dict, Any, Optional, Union
|
|
2
|
+
from puzzlekit.solvers import get_solver_class
|
|
3
|
+
from puzzlekit.parsers.registry import get_parser
|
|
4
|
+
from puzzlekit.formats.base import PuzzleInstance
|
|
5
|
+
from puzzlekit.formats.puzzlink_converter import PuzzlinkConverter
|
|
6
|
+
from puzzlekit.formats.penpa_converter import (
|
|
7
|
+
PenpaConverter,
|
|
8
|
+
PENPA_PREFIX,
|
|
9
|
+
PENPA_URLPREFIX,
|
|
10
|
+
PenpaDecodeError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
_FORMAT_ALIASES = {
|
|
15
|
+
"puzzlink": "puzzlink",
|
|
16
|
+
"pzl": "puzzlink",
|
|
17
|
+
"penpa": "penpa",
|
|
18
|
+
"ppa": "penpa",
|
|
19
|
+
"ir": "ir",
|
|
20
|
+
"instance": "ir",
|
|
21
|
+
"puzzleinstance": "ir",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _normalize_format_name(name: str) -> str:
|
|
26
|
+
if not isinstance(name, str) or not name.strip():
|
|
27
|
+
raise ValueError("Format name must be a non-empty string.")
|
|
28
|
+
key = name.strip().lower()
|
|
29
|
+
if key not in _FORMAT_ALIASES:
|
|
30
|
+
raise ValueError(f"Unknown format '{name}'. Supported: puzzlink, penpa, ir.")
|
|
31
|
+
return _FORMAT_ALIASES[key]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _normalize_penpa_source(source: str) -> str:
|
|
35
|
+
s = source.strip()
|
|
36
|
+
if s.startswith("#"):
|
|
37
|
+
s = s[1:]
|
|
38
|
+
if s.startswith(PENPA_PREFIX):
|
|
39
|
+
return s
|
|
40
|
+
if s.startswith(PENPA_URLPREFIX):
|
|
41
|
+
frag = s.split("#", 1)[1] if "#" in s else ""
|
|
42
|
+
if frag.startswith("#"):
|
|
43
|
+
frag = frag[1:]
|
|
44
|
+
return frag if frag.startswith(PENPA_PREFIX) else f"{PENPA_PREFIX}{frag}"
|
|
45
|
+
return s
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _detect_source_format(source: str) -> str:
|
|
49
|
+
s = source.strip()
|
|
50
|
+
if "puzz.link/p?" in s:
|
|
51
|
+
return "puzzlink"
|
|
52
|
+
if s.startswith(PENPA_PREFIX) or s.startswith("#" + PENPA_PREFIX) or PENPA_URLPREFIX in s:
|
|
53
|
+
return "penpa"
|
|
54
|
+
raise ValueError("Cannot detect source format. Please pass source_format explicitly.")
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _build_converter(fmt: str, converter_config: Optional[Dict[str, Any]] = None):
|
|
58
|
+
if fmt == "puzzlink":
|
|
59
|
+
return PuzzlinkConverter(converter_config or {})
|
|
60
|
+
if fmt == "penpa":
|
|
61
|
+
return PenpaConverter(converter_config or {})
|
|
62
|
+
raise ValueError(f"Unsupported converter format '{fmt}'.")
|
|
63
|
+
|
|
64
|
+
def solve(
|
|
65
|
+
source: Union[str, Dict[str, Any]],
|
|
66
|
+
puzzle_type: str,
|
|
67
|
+
**kwargs
|
|
68
|
+
) -> Any:
|
|
69
|
+
"""
|
|
70
|
+
Unified entry point for solving puzzles.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
source:
|
|
74
|
+
- A string containing the raw puzzle data (with headers, e.g. "9 9\n...").
|
|
75
|
+
- A dictionary (pre-parsed data).
|
|
76
|
+
puzzle_type: The snake_case type name (e.g., 'akari', 'fuzuli').
|
|
77
|
+
show: Whether to visualize the result.
|
|
78
|
+
**kwargs: Overrides for solver parameters.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
# --- 1. (Parsing) ---
|
|
82
|
+
init_params = {}
|
|
83
|
+
|
|
84
|
+
if isinstance(source, dict):
|
|
85
|
+
init_params = source.copy()
|
|
86
|
+
|
|
87
|
+
elif isinstance(source, str):
|
|
88
|
+
try:
|
|
89
|
+
target_parser = get_parser(puzzle_type)
|
|
90
|
+
parsed_data = target_parser(source.strip())
|
|
91
|
+
|
|
92
|
+
if parsed_data is None:
|
|
93
|
+
raise ValueError(f"Parser returned None for type '{puzzle_type}'")
|
|
94
|
+
|
|
95
|
+
init_params.update(parsed_data)
|
|
96
|
+
except ValueError as e:
|
|
97
|
+
raise ValueError(f"Parsing failed for type '{puzzle_type}': {e}")
|
|
98
|
+
|
|
99
|
+
else:
|
|
100
|
+
raise TypeError(f"Source must be dict or raw string, got {type(source)}")
|
|
101
|
+
|
|
102
|
+
init_params.update(kwargs)
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
SolverClass = get_solver_class(puzzle_type)
|
|
106
|
+
except ValueError as e:
|
|
107
|
+
raise ValueError(f"Unknown puzzle type '{puzzle_type}'.") from e
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
solver_instance = SolverClass(**init_params)
|
|
111
|
+
|
|
112
|
+
result = solver_instance.solve()
|
|
113
|
+
|
|
114
|
+
return result
|
|
115
|
+
|
|
116
|
+
def solver(puzzle_type: str, data: Dict[str, Any] = None, **kwargs) -> Any:
|
|
117
|
+
# return solve(source=data, puzzle_type=puzzle_type, **kwargs)
|
|
118
|
+
init_params = {}
|
|
119
|
+
|
|
120
|
+
if isinstance(data, dict):
|
|
121
|
+
init_params = data.copy()
|
|
122
|
+
else:
|
|
123
|
+
raise TypeError(f"Source must be dict or raw string, got {type(data)}")
|
|
124
|
+
|
|
125
|
+
init_params.update(kwargs)
|
|
126
|
+
|
|
127
|
+
try:
|
|
128
|
+
SolverClass = get_solver_class(puzzle_type)
|
|
129
|
+
except ValueError as e:
|
|
130
|
+
raise ValueError(f"Unknown puzzle type '{puzzle_type}'.") from e
|
|
131
|
+
return SolverClass(**init_params)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def decode(
|
|
135
|
+
source: str,
|
|
136
|
+
source_format: Optional[str] = None,
|
|
137
|
+
converter_config: Optional[Dict[str, Any]] = None,
|
|
138
|
+
) -> PuzzleInstance:
|
|
139
|
+
"""
|
|
140
|
+
Decode puzzle URL/text into PuzzleInstance (IR).
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
source: puzz.link URL, penpa URL, or penpa payload (`m=edit&p=...`).
|
|
144
|
+
source_format: Optional explicit source format ('puzzlink' or 'penpa').
|
|
145
|
+
converter_config: Optional converter config dict.
|
|
146
|
+
"""
|
|
147
|
+
if not isinstance(source, str):
|
|
148
|
+
raise TypeError(f"source must be str, got {type(source)}")
|
|
149
|
+
|
|
150
|
+
fmt = _normalize_format_name(source_format) if source_format else _detect_source_format(source)
|
|
151
|
+
if fmt == "ir":
|
|
152
|
+
raise ValueError("decode(source_format='ir') is not valid.")
|
|
153
|
+
|
|
154
|
+
converter = _build_converter(fmt, converter_config)
|
|
155
|
+
normalized_source = _normalize_penpa_source(source) if fmt == "penpa" else source
|
|
156
|
+
return converter.decode(normalized_source)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def encode(
|
|
160
|
+
instance: PuzzleInstance,
|
|
161
|
+
target_format: str,
|
|
162
|
+
converter_config: Optional[Dict[str, Any]] = None,
|
|
163
|
+
) -> str:
|
|
164
|
+
"""
|
|
165
|
+
Encode PuzzleInstance (IR) into target format URL/text.
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
instance: PuzzleInstance IR.
|
|
169
|
+
target_format: 'puzzlink' or 'penpa'.
|
|
170
|
+
converter_config: Optional converter config dict.
|
|
171
|
+
"""
|
|
172
|
+
if not isinstance(instance, PuzzleInstance):
|
|
173
|
+
raise TypeError(f"instance must be PuzzleInstance, got {type(instance)}")
|
|
174
|
+
|
|
175
|
+
fmt = _normalize_format_name(target_format)
|
|
176
|
+
if fmt == "ir":
|
|
177
|
+
raise ValueError("encode(target_format='ir') is not valid.")
|
|
178
|
+
|
|
179
|
+
converter = _build_converter(fmt, converter_config)
|
|
180
|
+
return converter.encode(instance)
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def convert(
|
|
184
|
+
source: Union[str, PuzzleInstance],
|
|
185
|
+
target_format: str,
|
|
186
|
+
source_format: Optional[str] = None,
|
|
187
|
+
converter_config: Optional[Dict[str, Any]] = None,
|
|
188
|
+
decode_converter_config: Optional[Dict[str, Any]] = None,
|
|
189
|
+
encode_converter_config: Optional[Dict[str, Any]] = None,
|
|
190
|
+
) -> Union[str, PuzzleInstance]:
|
|
191
|
+
"""
|
|
192
|
+
Convert between puzzlink/penpa/IR.
|
|
193
|
+
|
|
194
|
+
Examples:
|
|
195
|
+
- URL -> IR: convert(url, "ir")
|
|
196
|
+
- puzzlink -> penpa: convert(url, "penpa")
|
|
197
|
+
- penpa -> puzzlink: convert(url, "puzzlink")
|
|
198
|
+
- IR -> puzzlink: convert(ir, "puzzlink")
|
|
199
|
+
|
|
200
|
+
Notes:
|
|
201
|
+
- `converter_config` is kept for backward compatibility and applies to both
|
|
202
|
+
decode/encode when side-specific configs are not provided.
|
|
203
|
+
- `decode_converter_config` and `encode_converter_config` let callers pass
|
|
204
|
+
different settings per stage in a two-step conversion.
|
|
205
|
+
"""
|
|
206
|
+
dst = _normalize_format_name(target_format)
|
|
207
|
+
decode_cfg = decode_converter_config if decode_converter_config is not None else converter_config
|
|
208
|
+
encode_cfg = encode_converter_config if encode_converter_config is not None else converter_config
|
|
209
|
+
|
|
210
|
+
if isinstance(source, PuzzleInstance):
|
|
211
|
+
if dst == "ir":
|
|
212
|
+
return source
|
|
213
|
+
return encode(source, dst, converter_config=encode_cfg)
|
|
214
|
+
|
|
215
|
+
if not isinstance(source, str):
|
|
216
|
+
raise TypeError(f"source must be str or PuzzleInstance, got {type(source)}")
|
|
217
|
+
|
|
218
|
+
ir = decode(source, source_format=source_format, converter_config=decode_cfg)
|
|
219
|
+
if dst == "ir":
|
|
220
|
+
return ir
|
|
221
|
+
return encode(ir, dst, converter_config=encode_cfg)
|
|
222
|
+
|
|
223
|
+
__all__ = ["solve", "solver", "decode", "encode", "convert", "PenpaDecodeError"]
|
|
224
|
+
__version__ = '0.3.3'
|