chuk-puzzles-gym 0.9__tar.gz → 0.10.1__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.
- {chuk_puzzles_gym-0.9/src/chuk_puzzles_gym.egg-info → chuk_puzzles_gym-0.10.1}/PKG-INFO +60 -19
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/README.md +58 -17
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/pyproject.toml +2 -2
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/eval.py +21 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/__init__.py +22 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/binary/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/bridges/game.py +2 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/cryptarithmetic/__init__.py +7 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/cryptarithmetic/commands.py +75 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/cryptarithmetic/config.py +23 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/cryptarithmetic/game.py +388 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/einstein/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/fillomino/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/futoshiki/game.py +2 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/graph_coloring/__init__.py +7 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/graph_coloring/commands.py +96 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/graph_coloring/config.py +24 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/graph_coloring/game.py +316 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hidato/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hitori/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kakuro/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kenken/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/killer_sudoku/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/knapsack/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/lights_out/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/logic_grid/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/mastermind/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/minesweeper/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nonogram/game.py +2 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/nqueens/__init__.py +6 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/nqueens/config.py +23 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/nqueens/game.py +321 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/numberlink/__init__.py +6 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/numberlink/config.py +23 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/numberlink/game.py +344 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nurikabe/game.py +2 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/rush_hour/__init__.py +8 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/rush_hour/commands.py +57 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/rush_hour/config.py +25 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/rush_hour/game.py +479 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/rush_hour/models.py +15 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/shikaku/game.py +2 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/skyscrapers/__init__.py +6 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/skyscrapers/config.py +22 -0
- chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym/games/skyscrapers/game.py +282 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/slitherlink/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sokoban/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/star_battle/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sudoku/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/tents/game.py +2 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/server.py +18 -70
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/trace/generator.py +87 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1/src/chuk_puzzles_gym.egg-info}/PKG-INFO +60 -19
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym.egg-info/SOURCES.txt +28 -0
- chuk_puzzles_gym-0.10.1/tests/test_cryptarithmetic_game.py +231 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_deterministic_seeding.py +10 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_game_configs.py +135 -0
- chuk_puzzles_gym-0.10.1/tests/test_graph_coloring_game.py +223 -0
- chuk_puzzles_gym-0.10.1/tests/test_nqueens_game.py +206 -0
- chuk_puzzles_gym-0.10.1/tests/test_numberlink_game.py +157 -0
- chuk_puzzles_gym-0.10.1/tests/test_rush_hour_game.py +236 -0
- chuk_puzzles_gym-0.10.1/tests/test_skyscrapers_game.py +197 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/MANIFEST.in +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/config.yaml +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/setup.cfg +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/constants.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/export/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/export/dataset.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/_base/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/_base/commands.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/_base/game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/binary/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/binary/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/bridges/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/bridges/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/einstein/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/einstein/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/einstein/constants.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/einstein/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/fillomino/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/fillomino/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/futoshiki/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/futoshiki/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hidato/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hidato/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hitori/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/hitori/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kakuro/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kakuro/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kenken/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kenken/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kenken/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/kenken/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/killer_sudoku/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/killer_sudoku/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/killer_sudoku/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/knapsack/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/knapsack/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/knapsack/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/knapsack/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/lights_out/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/lights_out/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/logic_grid/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/logic_grid/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/logic_grid/constants.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/logic_grid/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/mastermind/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/mastermind/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/minesweeper/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/minesweeper/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/minesweeper/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nonogram/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nonogram/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nurikabe/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nurikabe/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/nurikabe/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/constants.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/scheduler/models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/shikaku/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/shikaku/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/slitherlink/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/slitherlink/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sokoban/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sokoban/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/star_battle/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/star_battle/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sudoku/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sudoku/commands.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/sudoku/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/tents/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/games/tents/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/gym_env.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/base.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/config.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/enums.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/evaluation.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/models/games.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/trace/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym/utils/__init__.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym.egg-info/dependency_links.txt +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym.egg-info/entry_points.txt +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym.egg-info/requires.txt +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/src/chuk_puzzles_gym.egg-info/top_level.txt +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_base_models.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_binary_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_bridges.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_command_handlers.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_einstein.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_eval.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_fillomino.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_futoshiki_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_gym_env.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_hidato.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_hitori.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_kakuro_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_kenken_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_killer_sudoku.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_knapsack.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_lights_out.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_logic_grid_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_mastermind.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_minesweeper.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_nonogram_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_nurikabe.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_puzzle_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_scheduler.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_shikaku.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_slitherlink.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_sokoban.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_star_battle.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_sudoku_game.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_tents.py +0 -0
- {chuk_puzzles_gym-0.9 → chuk_puzzles_gym-0.10.1}/tests/test_trace_generator.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chuk-puzzles-gym
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Multi-game puzzle gym for LLM training and benchmarking -
|
|
3
|
+
Version: 0.10.1
|
|
4
|
+
Summary: Multi-game puzzle gym for LLM training and benchmarking - 30 constraint puzzles with synthetic data generation
|
|
5
5
|
Author: Chris Hay
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/chrishayuk/chuk-puzzles-gym
|
|
@@ -39,7 +39,7 @@ Requires-Dist: ipython>=8.14.0; extra == "dev"
|
|
|
39
39
|
[](https://docs.pydantic.dev/)
|
|
40
40
|
[](http://mypy-lang.org/)
|
|
41
41
|
|
|
42
|
-
A **multi-game puzzle gym** for **LLM training and benchmarking**, hosting
|
|
42
|
+
A **multi-game puzzle gym** for **LLM training and benchmarking**, hosting 30 different logic puzzle types with synthetic data generation. Built using [chuk-gym-core](https://github.com/chrishayuk/chuk-gym-core) and [chuk-protocol-server](https://github.com/chrishayuk/chuk-protocol-server).
|
|
43
43
|
|
|
44
44
|
**Perfect for:**
|
|
45
45
|
- 🤖 **LLM Agent Testing** - Benchmark reasoning capabilities across constraint types
|
|
@@ -82,12 +82,13 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
82
82
|
|
|
83
83
|
## Features
|
|
84
84
|
|
|
85
|
-
- **
|
|
85
|
+
- **30 Puzzle Games** with three difficulty levels each (easy, medium, hard)
|
|
86
86
|
- **7 Classic Logic Puzzles** - Sudoku, KenKen, Kakuro, Binary, Futoshiki, Nonogram, Logic Grid
|
|
87
87
|
- **7 Advanced CP-SAT Puzzles** - Killer Sudoku, Lights Out, Mastermind, Slitherlink, Bridges, Hitori, Shikaku
|
|
88
88
|
- **5 Specialized Constraint Puzzles** - Hidato, Tents and Trees, Fillomino, Star Battle, Sokoban
|
|
89
89
|
- **2 Optimization Challenges** - Knapsack, Task Scheduler
|
|
90
90
|
- **3 Advanced Reasoning Puzzles** - Nurikabe, Einstein's Puzzle, Minesweeper
|
|
91
|
+
- **6 Combinatorial & Search Puzzles** - Skyscrapers, N-Queens, Numberlink, Graph Coloring, Cryptarithmetic, Rush Hour
|
|
91
92
|
- **Agent-Friendly Mode** - Structured output with clear markers for AI agents and tools
|
|
92
93
|
- Enable with `mode agent` command
|
|
93
94
|
- Machine-parseable grid format with clear start/end markers
|
|
@@ -113,7 +114,7 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
113
114
|
- **Clean ASCII art grids** - perfectly aligned for easy parsing
|
|
114
115
|
- **Deterministic seeding** - Replay any puzzle with the same seed
|
|
115
116
|
- **Gymnasium-compatible RL Environment** (`PuzzleEnv`) for training agents
|
|
116
|
-
- **Comprehensive test suite** (
|
|
117
|
+
- **Comprehensive test suite** (1323 tests, 94% coverage)
|
|
117
118
|
- **Modern Python best practices:**
|
|
118
119
|
- **Pydantic v2 native** - All models use ConfigDict for type safety
|
|
119
120
|
- **Async native** - Full async/await support throughout
|
|
@@ -173,6 +174,17 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
173
174
|
| **Einstein's Puzzle** | 5 houses × 5 attributes | Multi-attribute deduction + Logic chains | ✅ Complete |
|
|
174
175
|
| **Minesweeper** | 6×6 to 10×10 | Probabilistic reasoning + Safe deduction | ✅ Complete |
|
|
175
176
|
|
|
177
|
+
### Combinatorial & Search Puzzles
|
|
178
|
+
|
|
179
|
+
| Game | Grid Size | Constraint Types | Status |
|
|
180
|
+
|------|-----------|------------------|--------|
|
|
181
|
+
| **Skyscrapers** | 4×4 to 6×6 | Latin square + Visibility clues from 4 borders | ✅ Complete |
|
|
182
|
+
| **N-Queens** | 6×6 to 12×12 | Placement + Row/Column/Diagonal attack avoidance | ✅ Complete |
|
|
183
|
+
| **Numberlink** | 5×5 to 9×9 | Path connectivity + Non-crossing + Space filling | ✅ Complete |
|
|
184
|
+
| **Graph Coloring** | 6-15 nodes | Graph coloring + Inequality + Global constraint | ✅ Complete |
|
|
185
|
+
| **Cryptarithmetic** | 3-5 digit words | Arithmetic + AllDifferent + Carry propagation | ✅ Complete |
|
|
186
|
+
| **Rush Hour** | 6×6 | Sequential planning + Spatial blocking + Search | ✅ Complete |
|
|
187
|
+
|
|
176
188
|
## Solver Profiles & Business Mapping
|
|
177
189
|
|
|
178
190
|
Each game includes metadata for **constraint types**, **business analogies**, and **complexity profiles**, making it easy to:
|
|
@@ -216,7 +228,13 @@ resource_games = [
|
|
|
216
228
|
| **Global Loop** | Slitherlink | Circuit design, Path finding |
|
|
217
229
|
| **Boolean SAT** | Lights Out | Feature dependencies, Toggle systems |
|
|
218
230
|
| **Cage Sums** | Killer Sudoku, Kakuro | Team budgets, Grouped constraints |
|
|
219
|
-
| **AllDifferent** | Sudoku, KenKen | Resource uniqueness, Assignment problems |
|
|
231
|
+
| **AllDifferent** | Sudoku, KenKen, Skyscrapers | Resource uniqueness, Assignment problems |
|
|
232
|
+
| **Visibility/Ordering** | Skyscrapers | Priority ranking, Stack-based processing |
|
|
233
|
+
| **Attack Avoidance** | N-Queens, Star Battle | Non-conflicting resource placement |
|
|
234
|
+
| **Path Connectivity** | Numberlink, Nurikabe | Network routing, Cable layout |
|
|
235
|
+
| **Graph Coloring** | Graph Coloring | Frequency assignment, Register allocation, Scheduling |
|
|
236
|
+
| **Arithmetic Deduction** | Cryptarithmetic, KenKen | Code breaking, Constraint propagation |
|
|
237
|
+
| **Sequential Planning** | Rush Hour, Sokoban | Logistics planning, Deadlock resolution |
|
|
220
238
|
|
|
221
239
|
## Quick Start
|
|
222
240
|
|
|
@@ -397,6 +415,14 @@ ADVANCED REASONING PUZZLES:
|
|
|
397
415
|
23) Einstein's Puzzle - Who owns the fish? Multi-attribute deduction
|
|
398
416
|
24) Minesweeper - Find all mines using logical deduction
|
|
399
417
|
|
|
418
|
+
COMBINATORIAL & SEARCH PUZZLES:
|
|
419
|
+
25) Skyscrapers - Latin square with visibility clues from borders
|
|
420
|
+
26) N-Queens - Place queens with no row/column/diagonal conflicts
|
|
421
|
+
27) Numberlink - Connect pairs with non-crossing paths filling the grid
|
|
422
|
+
28) Graph Coloring - Color nodes so no adjacent pair shares a color
|
|
423
|
+
29) Cryptarithmetic - Assign digits to letters to satisfy an equation
|
|
424
|
+
30) Rush Hour - Slide vehicles to free the target car to the exit
|
|
425
|
+
|
|
400
426
|
Commands:
|
|
401
427
|
<number> - Select game by number
|
|
402
428
|
<name> - Select game by name (e.g., 'sudoku')
|
|
@@ -453,7 +479,7 @@ The project includes a **Gymnasium-compatible environment** for training reinfor
|
|
|
453
479
|
```python
|
|
454
480
|
from chuk_puzzles_gym.gym_env import PuzzleEnv
|
|
455
481
|
|
|
456
|
-
# Create environment for any of the
|
|
482
|
+
# Create environment for any of the 30 games
|
|
457
483
|
env = PuzzleEnv("sudoku", difficulty="easy", seed=42)
|
|
458
484
|
|
|
459
485
|
# Reset to start a new episode
|
|
@@ -472,7 +498,7 @@ games = PuzzleEnv.available_games()
|
|
|
472
498
|
|
|
473
499
|
### Features
|
|
474
500
|
|
|
475
|
-
- **All
|
|
501
|
+
- **All 30 games** accessible through unified API
|
|
476
502
|
- **Configurable rewards** for correct moves, invalid attempts, completion bonuses
|
|
477
503
|
- **Hint system** with optional budget limits
|
|
478
504
|
- **Solver-free mode** for pure reasoning benchmarks
|
|
@@ -586,7 +612,7 @@ Generate synthetic puzzle datasets for training and benchmarking LLMs and constr
|
|
|
586
612
|
### CLI Usage
|
|
587
613
|
|
|
588
614
|
```bash
|
|
589
|
-
# Generate 100 puzzles per game/difficulty for all
|
|
615
|
+
# Generate 100 puzzles per game/difficulty for all 30 games
|
|
590
616
|
chuk-puzzles-export -o puzzles.jsonl
|
|
591
617
|
|
|
592
618
|
# Specific games only
|
|
@@ -744,6 +770,9 @@ When `include_trace=True` (default), each problem includes step-by-step solution
|
|
|
744
770
|
| Hitori | `duplicate_elimination` |
|
|
745
771
|
| Bridges | `connectivity_constraint` |
|
|
746
772
|
| Slitherlink | `loop_constraint` |
|
|
773
|
+
| Graph Coloring | `graph_coloring_constraint` |
|
|
774
|
+
| Cryptarithmetic | `arithmetic_constraint` |
|
|
775
|
+
| Rush Hour | `sequential_planning` |
|
|
747
776
|
| Others | `constraint_propagation` |
|
|
748
777
|
|
|
749
778
|
### Example: Generate Training Data
|
|
@@ -772,9 +801,9 @@ With default settings (`-n 100` per game/difficulty):
|
|
|
772
801
|
|
|
773
802
|
| Configuration | Problems Generated |
|
|
774
803
|
|--------------|-------------------|
|
|
775
|
-
| All games, all difficulties |
|
|
804
|
+
| All games, all difficulties | 30 games × 3 difficulties × 100 = 9,000 |
|
|
776
805
|
| Single game, all difficulties | 1 × 3 × 100 = 300 |
|
|
777
|
-
| All games, single difficulty |
|
|
806
|
+
| All games, single difficulty | 30 × 1 × 100 = 3,000 |
|
|
778
807
|
|
|
779
808
|
### Integration with chuk-gym-core
|
|
780
809
|
|
|
@@ -983,7 +1012,7 @@ pip install -e ".[dev]"
|
|
|
983
1012
|
|
|
984
1013
|
### Testing
|
|
985
1014
|
|
|
986
|
-
The project has comprehensive test coverage (94%,
|
|
1015
|
+
The project has comprehensive test coverage (94%, 1323 tests):
|
|
987
1016
|
|
|
988
1017
|
```bash
|
|
989
1018
|
# Run all tests
|
|
@@ -1034,7 +1063,7 @@ The project follows modern Python best practices with a **9.8/10 compliance scor
|
|
|
1034
1063
|
- ✅ **Test Coverage** (9.5/10) - 94% overall, most files ≥90%
|
|
1035
1064
|
|
|
1036
1065
|
#### Quality Metrics
|
|
1037
|
-
- **
|
|
1066
|
+
- **1323 tests** - All passing ✅
|
|
1038
1067
|
- **94% coverage** - Exceeds 90% threshold ✅
|
|
1039
1068
|
- **Zero linting errors** - Clean codebase ✅
|
|
1040
1069
|
- **Full type safety** - MyPy passes ✅
|
|
@@ -1181,7 +1210,7 @@ chuk-puzzles-gym/
|
|
|
1181
1210
|
│ │ ├── __init__.py
|
|
1182
1211
|
│ │ ├── game.py
|
|
1183
1212
|
│ │ └── config.py
|
|
1184
|
-
│ └── ... (
|
|
1213
|
+
│ └── ... (30 games total)
|
|
1185
1214
|
├── tests/
|
|
1186
1215
|
│ ├── test_puzzle_game.py # Base class tests
|
|
1187
1216
|
│ ├── test_deterministic_seeding.py # Seeding tests
|
|
@@ -1191,6 +1220,12 @@ chuk-puzzles-gym/
|
|
|
1191
1220
|
├── examples/
|
|
1192
1221
|
│ ├── simple_client.py # Telnet client example
|
|
1193
1222
|
│ ├── websocket_client.py # WebSocket client example
|
|
1223
|
+
│ ├── example_skyscrapers.py # Skyscrapers game logic demo
|
|
1224
|
+
│ ├── example_nqueens.py # N-Queens game logic demo
|
|
1225
|
+
│ ├── example_numberlink.py # Numberlink game logic demo
|
|
1226
|
+
│ ├── example_graph_coloring.py # Graph Coloring game logic demo
|
|
1227
|
+
│ ├── example_cryptarithmetic.py# Cryptarithmetic game logic demo
|
|
1228
|
+
│ ├── example_rush_hour.py # Rush Hour game logic demo
|
|
1194
1229
|
│ └── README.md # Example usage guide
|
|
1195
1230
|
├── .github/workflows/ # CI/CD workflows
|
|
1196
1231
|
├── pyproject.toml # Modern Python project config
|
|
@@ -1203,14 +1238,15 @@ chuk-puzzles-gym/
|
|
|
1203
1238
|
|
|
1204
1239
|
### Key Statistics
|
|
1205
1240
|
|
|
1206
|
-
- **Test Coverage**: 94% overall (
|
|
1241
|
+
- **Test Coverage**: 94% overall (1323 tests, all passing)
|
|
1207
1242
|
- **Code Quality Score**: 9.8/10 (near perfect compliance)
|
|
1208
|
-
- **Games Implemented**:
|
|
1243
|
+
- **Games Implemented**: 30 complete puzzle types
|
|
1209
1244
|
- 7 Classic Logic Puzzles
|
|
1210
1245
|
- 7 Advanced CP-SAT Puzzles
|
|
1211
1246
|
- 5 Specialized Constraint Puzzles
|
|
1212
1247
|
- 2 Optimization Challenges
|
|
1213
1248
|
- 3 Advanced Reasoning Puzzles
|
|
1249
|
+
- 6 Combinatorial & Search Puzzles
|
|
1214
1250
|
- **Supported Transports**: 4 (Telnet, TCP, WebSocket, WS-Telnet)
|
|
1215
1251
|
- **Agent-Friendly Mode**: Structured output for AI tools
|
|
1216
1252
|
- **Gymnasium API**: RL-compatible environment for all games
|
|
@@ -1243,7 +1279,7 @@ Test the generality of constraint solvers (like MCP solvers):
|
|
|
1243
1279
|
|
|
1244
1280
|
Learn about constraint satisfaction problems:
|
|
1245
1281
|
|
|
1246
|
-
- **
|
|
1282
|
+
- **30 different puzzle types** demonstrating various constraint types:
|
|
1247
1283
|
- AllDifferent constraints (Sudoku, KenKen, Futoshiki)
|
|
1248
1284
|
- Arithmetic constraints (KenKen, Kakuro, Killer Sudoku)
|
|
1249
1285
|
- Boolean/SAT constraints (Lights Out, Binary Puzzle)
|
|
@@ -1253,9 +1289,14 @@ Learn about constraint satisfaction problems:
|
|
|
1253
1289
|
- Temporal reasoning (Task Scheduler)
|
|
1254
1290
|
- Connectivity constraints (Nurikabe, Slitherlink)
|
|
1255
1291
|
- Probabilistic reasoning (Minesweeper)
|
|
1256
|
-
-
|
|
1292
|
+
- Graph coloring (Graph Coloring)
|
|
1293
|
+
- Arithmetic deduction (Cryptarithmetic)
|
|
1294
|
+
- Sequential planning (Rush Hour)
|
|
1295
|
+
- Visibility constraints (Skyscrapers)
|
|
1296
|
+
- Attack avoidance (N-Queens)
|
|
1297
|
+
- Path connectivity (Numberlink)
|
|
1257
1298
|
- **Well-documented code** showing puzzle generation algorithms
|
|
1258
|
-
- **Comprehensive tests** (
|
|
1299
|
+
- **Comprehensive tests** (1323 tests, 94% coverage) demonstrating validation
|
|
1259
1300
|
- **Deterministic seeding** - Reproduce any puzzle for debugging/testing
|
|
1260
1301
|
- **Production-ready** - 9.8/10 code quality score
|
|
1261
1302
|
- **Type-safe** - Full Pydantic v2 and MyPy compliance
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
[](https://docs.pydantic.dev/)
|
|
9
9
|
[](http://mypy-lang.org/)
|
|
10
10
|
|
|
11
|
-
A **multi-game puzzle gym** for **LLM training and benchmarking**, hosting
|
|
11
|
+
A **multi-game puzzle gym** for **LLM training and benchmarking**, hosting 30 different logic puzzle types with synthetic data generation. Built using [chuk-gym-core](https://github.com/chrishayuk/chuk-gym-core) and [chuk-protocol-server](https://github.com/chrishayuk/chuk-protocol-server).
|
|
12
12
|
|
|
13
13
|
**Perfect for:**
|
|
14
14
|
- 🤖 **LLM Agent Testing** - Benchmark reasoning capabilities across constraint types
|
|
@@ -51,12 +51,13 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
51
51
|
|
|
52
52
|
## Features
|
|
53
53
|
|
|
54
|
-
- **
|
|
54
|
+
- **30 Puzzle Games** with three difficulty levels each (easy, medium, hard)
|
|
55
55
|
- **7 Classic Logic Puzzles** - Sudoku, KenKen, Kakuro, Binary, Futoshiki, Nonogram, Logic Grid
|
|
56
56
|
- **7 Advanced CP-SAT Puzzles** - Killer Sudoku, Lights Out, Mastermind, Slitherlink, Bridges, Hitori, Shikaku
|
|
57
57
|
- **5 Specialized Constraint Puzzles** - Hidato, Tents and Trees, Fillomino, Star Battle, Sokoban
|
|
58
58
|
- **2 Optimization Challenges** - Knapsack, Task Scheduler
|
|
59
59
|
- **3 Advanced Reasoning Puzzles** - Nurikabe, Einstein's Puzzle, Minesweeper
|
|
60
|
+
- **6 Combinatorial & Search Puzzles** - Skyscrapers, N-Queens, Numberlink, Graph Coloring, Cryptarithmetic, Rush Hour
|
|
60
61
|
- **Agent-Friendly Mode** - Structured output with clear markers for AI agents and tools
|
|
61
62
|
- Enable with `mode agent` command
|
|
62
63
|
- Machine-parseable grid format with clear start/end markers
|
|
@@ -82,7 +83,7 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
82
83
|
- **Clean ASCII art grids** - perfectly aligned for easy parsing
|
|
83
84
|
- **Deterministic seeding** - Replay any puzzle with the same seed
|
|
84
85
|
- **Gymnasium-compatible RL Environment** (`PuzzleEnv`) for training agents
|
|
85
|
-
- **Comprehensive test suite** (
|
|
86
|
+
- **Comprehensive test suite** (1323 tests, 94% coverage)
|
|
86
87
|
- **Modern Python best practices:**
|
|
87
88
|
- **Pydantic v2 native** - All models use ConfigDict for type safety
|
|
88
89
|
- **Async native** - Full async/await support throughout
|
|
@@ -142,6 +143,17 @@ Once connected, type `help` to see available games, or `sudoku easy` to start pl
|
|
|
142
143
|
| **Einstein's Puzzle** | 5 houses × 5 attributes | Multi-attribute deduction + Logic chains | ✅ Complete |
|
|
143
144
|
| **Minesweeper** | 6×6 to 10×10 | Probabilistic reasoning + Safe deduction | ✅ Complete |
|
|
144
145
|
|
|
146
|
+
### Combinatorial & Search Puzzles
|
|
147
|
+
|
|
148
|
+
| Game | Grid Size | Constraint Types | Status |
|
|
149
|
+
|------|-----------|------------------|--------|
|
|
150
|
+
| **Skyscrapers** | 4×4 to 6×6 | Latin square + Visibility clues from 4 borders | ✅ Complete |
|
|
151
|
+
| **N-Queens** | 6×6 to 12×12 | Placement + Row/Column/Diagonal attack avoidance | ✅ Complete |
|
|
152
|
+
| **Numberlink** | 5×5 to 9×9 | Path connectivity + Non-crossing + Space filling | ✅ Complete |
|
|
153
|
+
| **Graph Coloring** | 6-15 nodes | Graph coloring + Inequality + Global constraint | ✅ Complete |
|
|
154
|
+
| **Cryptarithmetic** | 3-5 digit words | Arithmetic + AllDifferent + Carry propagation | ✅ Complete |
|
|
155
|
+
| **Rush Hour** | 6×6 | Sequential planning + Spatial blocking + Search | ✅ Complete |
|
|
156
|
+
|
|
145
157
|
## Solver Profiles & Business Mapping
|
|
146
158
|
|
|
147
159
|
Each game includes metadata for **constraint types**, **business analogies**, and **complexity profiles**, making it easy to:
|
|
@@ -185,7 +197,13 @@ resource_games = [
|
|
|
185
197
|
| **Global Loop** | Slitherlink | Circuit design, Path finding |
|
|
186
198
|
| **Boolean SAT** | Lights Out | Feature dependencies, Toggle systems |
|
|
187
199
|
| **Cage Sums** | Killer Sudoku, Kakuro | Team budgets, Grouped constraints |
|
|
188
|
-
| **AllDifferent** | Sudoku, KenKen | Resource uniqueness, Assignment problems |
|
|
200
|
+
| **AllDifferent** | Sudoku, KenKen, Skyscrapers | Resource uniqueness, Assignment problems |
|
|
201
|
+
| **Visibility/Ordering** | Skyscrapers | Priority ranking, Stack-based processing |
|
|
202
|
+
| **Attack Avoidance** | N-Queens, Star Battle | Non-conflicting resource placement |
|
|
203
|
+
| **Path Connectivity** | Numberlink, Nurikabe | Network routing, Cable layout |
|
|
204
|
+
| **Graph Coloring** | Graph Coloring | Frequency assignment, Register allocation, Scheduling |
|
|
205
|
+
| **Arithmetic Deduction** | Cryptarithmetic, KenKen | Code breaking, Constraint propagation |
|
|
206
|
+
| **Sequential Planning** | Rush Hour, Sokoban | Logistics planning, Deadlock resolution |
|
|
189
207
|
|
|
190
208
|
## Quick Start
|
|
191
209
|
|
|
@@ -366,6 +384,14 @@ ADVANCED REASONING PUZZLES:
|
|
|
366
384
|
23) Einstein's Puzzle - Who owns the fish? Multi-attribute deduction
|
|
367
385
|
24) Minesweeper - Find all mines using logical deduction
|
|
368
386
|
|
|
387
|
+
COMBINATORIAL & SEARCH PUZZLES:
|
|
388
|
+
25) Skyscrapers - Latin square with visibility clues from borders
|
|
389
|
+
26) N-Queens - Place queens with no row/column/diagonal conflicts
|
|
390
|
+
27) Numberlink - Connect pairs with non-crossing paths filling the grid
|
|
391
|
+
28) Graph Coloring - Color nodes so no adjacent pair shares a color
|
|
392
|
+
29) Cryptarithmetic - Assign digits to letters to satisfy an equation
|
|
393
|
+
30) Rush Hour - Slide vehicles to free the target car to the exit
|
|
394
|
+
|
|
369
395
|
Commands:
|
|
370
396
|
<number> - Select game by number
|
|
371
397
|
<name> - Select game by name (e.g., 'sudoku')
|
|
@@ -422,7 +448,7 @@ The project includes a **Gymnasium-compatible environment** for training reinfor
|
|
|
422
448
|
```python
|
|
423
449
|
from chuk_puzzles_gym.gym_env import PuzzleEnv
|
|
424
450
|
|
|
425
|
-
# Create environment for any of the
|
|
451
|
+
# Create environment for any of the 30 games
|
|
426
452
|
env = PuzzleEnv("sudoku", difficulty="easy", seed=42)
|
|
427
453
|
|
|
428
454
|
# Reset to start a new episode
|
|
@@ -441,7 +467,7 @@ games = PuzzleEnv.available_games()
|
|
|
441
467
|
|
|
442
468
|
### Features
|
|
443
469
|
|
|
444
|
-
- **All
|
|
470
|
+
- **All 30 games** accessible through unified API
|
|
445
471
|
- **Configurable rewards** for correct moves, invalid attempts, completion bonuses
|
|
446
472
|
- **Hint system** with optional budget limits
|
|
447
473
|
- **Solver-free mode** for pure reasoning benchmarks
|
|
@@ -555,7 +581,7 @@ Generate synthetic puzzle datasets for training and benchmarking LLMs and constr
|
|
|
555
581
|
### CLI Usage
|
|
556
582
|
|
|
557
583
|
```bash
|
|
558
|
-
# Generate 100 puzzles per game/difficulty for all
|
|
584
|
+
# Generate 100 puzzles per game/difficulty for all 30 games
|
|
559
585
|
chuk-puzzles-export -o puzzles.jsonl
|
|
560
586
|
|
|
561
587
|
# Specific games only
|
|
@@ -713,6 +739,9 @@ When `include_trace=True` (default), each problem includes step-by-step solution
|
|
|
713
739
|
| Hitori | `duplicate_elimination` |
|
|
714
740
|
| Bridges | `connectivity_constraint` |
|
|
715
741
|
| Slitherlink | `loop_constraint` |
|
|
742
|
+
| Graph Coloring | `graph_coloring_constraint` |
|
|
743
|
+
| Cryptarithmetic | `arithmetic_constraint` |
|
|
744
|
+
| Rush Hour | `sequential_planning` |
|
|
716
745
|
| Others | `constraint_propagation` |
|
|
717
746
|
|
|
718
747
|
### Example: Generate Training Data
|
|
@@ -741,9 +770,9 @@ With default settings (`-n 100` per game/difficulty):
|
|
|
741
770
|
|
|
742
771
|
| Configuration | Problems Generated |
|
|
743
772
|
|--------------|-------------------|
|
|
744
|
-
| All games, all difficulties |
|
|
773
|
+
| All games, all difficulties | 30 games × 3 difficulties × 100 = 9,000 |
|
|
745
774
|
| Single game, all difficulties | 1 × 3 × 100 = 300 |
|
|
746
|
-
| All games, single difficulty |
|
|
775
|
+
| All games, single difficulty | 30 × 1 × 100 = 3,000 |
|
|
747
776
|
|
|
748
777
|
### Integration with chuk-gym-core
|
|
749
778
|
|
|
@@ -952,7 +981,7 @@ pip install -e ".[dev]"
|
|
|
952
981
|
|
|
953
982
|
### Testing
|
|
954
983
|
|
|
955
|
-
The project has comprehensive test coverage (94%,
|
|
984
|
+
The project has comprehensive test coverage (94%, 1323 tests):
|
|
956
985
|
|
|
957
986
|
```bash
|
|
958
987
|
# Run all tests
|
|
@@ -1003,7 +1032,7 @@ The project follows modern Python best practices with a **9.8/10 compliance scor
|
|
|
1003
1032
|
- ✅ **Test Coverage** (9.5/10) - 94% overall, most files ≥90%
|
|
1004
1033
|
|
|
1005
1034
|
#### Quality Metrics
|
|
1006
|
-
- **
|
|
1035
|
+
- **1323 tests** - All passing ✅
|
|
1007
1036
|
- **94% coverage** - Exceeds 90% threshold ✅
|
|
1008
1037
|
- **Zero linting errors** - Clean codebase ✅
|
|
1009
1038
|
- **Full type safety** - MyPy passes ✅
|
|
@@ -1150,7 +1179,7 @@ chuk-puzzles-gym/
|
|
|
1150
1179
|
│ │ ├── __init__.py
|
|
1151
1180
|
│ │ ├── game.py
|
|
1152
1181
|
│ │ └── config.py
|
|
1153
|
-
│ └── ... (
|
|
1182
|
+
│ └── ... (30 games total)
|
|
1154
1183
|
├── tests/
|
|
1155
1184
|
│ ├── test_puzzle_game.py # Base class tests
|
|
1156
1185
|
│ ├── test_deterministic_seeding.py # Seeding tests
|
|
@@ -1160,6 +1189,12 @@ chuk-puzzles-gym/
|
|
|
1160
1189
|
├── examples/
|
|
1161
1190
|
│ ├── simple_client.py # Telnet client example
|
|
1162
1191
|
│ ├── websocket_client.py # WebSocket client example
|
|
1192
|
+
│ ├── example_skyscrapers.py # Skyscrapers game logic demo
|
|
1193
|
+
│ ├── example_nqueens.py # N-Queens game logic demo
|
|
1194
|
+
│ ├── example_numberlink.py # Numberlink game logic demo
|
|
1195
|
+
│ ├── example_graph_coloring.py # Graph Coloring game logic demo
|
|
1196
|
+
│ ├── example_cryptarithmetic.py# Cryptarithmetic game logic demo
|
|
1197
|
+
│ ├── example_rush_hour.py # Rush Hour game logic demo
|
|
1163
1198
|
│ └── README.md # Example usage guide
|
|
1164
1199
|
├── .github/workflows/ # CI/CD workflows
|
|
1165
1200
|
├── pyproject.toml # Modern Python project config
|
|
@@ -1172,14 +1207,15 @@ chuk-puzzles-gym/
|
|
|
1172
1207
|
|
|
1173
1208
|
### Key Statistics
|
|
1174
1209
|
|
|
1175
|
-
- **Test Coverage**: 94% overall (
|
|
1210
|
+
- **Test Coverage**: 94% overall (1323 tests, all passing)
|
|
1176
1211
|
- **Code Quality Score**: 9.8/10 (near perfect compliance)
|
|
1177
|
-
- **Games Implemented**:
|
|
1212
|
+
- **Games Implemented**: 30 complete puzzle types
|
|
1178
1213
|
- 7 Classic Logic Puzzles
|
|
1179
1214
|
- 7 Advanced CP-SAT Puzzles
|
|
1180
1215
|
- 5 Specialized Constraint Puzzles
|
|
1181
1216
|
- 2 Optimization Challenges
|
|
1182
1217
|
- 3 Advanced Reasoning Puzzles
|
|
1218
|
+
- 6 Combinatorial & Search Puzzles
|
|
1183
1219
|
- **Supported Transports**: 4 (Telnet, TCP, WebSocket, WS-Telnet)
|
|
1184
1220
|
- **Agent-Friendly Mode**: Structured output for AI tools
|
|
1185
1221
|
- **Gymnasium API**: RL-compatible environment for all games
|
|
@@ -1212,7 +1248,7 @@ Test the generality of constraint solvers (like MCP solvers):
|
|
|
1212
1248
|
|
|
1213
1249
|
Learn about constraint satisfaction problems:
|
|
1214
1250
|
|
|
1215
|
-
- **
|
|
1251
|
+
- **30 different puzzle types** demonstrating various constraint types:
|
|
1216
1252
|
- AllDifferent constraints (Sudoku, KenKen, Futoshiki)
|
|
1217
1253
|
- Arithmetic constraints (KenKen, Kakuro, Killer Sudoku)
|
|
1218
1254
|
- Boolean/SAT constraints (Lights Out, Binary Puzzle)
|
|
@@ -1222,9 +1258,14 @@ Learn about constraint satisfaction problems:
|
|
|
1222
1258
|
- Temporal reasoning (Task Scheduler)
|
|
1223
1259
|
- Connectivity constraints (Nurikabe, Slitherlink)
|
|
1224
1260
|
- Probabilistic reasoning (Minesweeper)
|
|
1225
|
-
-
|
|
1261
|
+
- Graph coloring (Graph Coloring)
|
|
1262
|
+
- Arithmetic deduction (Cryptarithmetic)
|
|
1263
|
+
- Sequential planning (Rush Hour)
|
|
1264
|
+
- Visibility constraints (Skyscrapers)
|
|
1265
|
+
- Attack avoidance (N-Queens)
|
|
1266
|
+
- Path connectivity (Numberlink)
|
|
1226
1267
|
- **Well-documented code** showing puzzle generation algorithms
|
|
1227
|
-
- **Comprehensive tests** (
|
|
1268
|
+
- **Comprehensive tests** (1323 tests, 94% coverage) demonstrating validation
|
|
1228
1269
|
- **Deterministic seeding** - Reproduce any puzzle for debugging/testing
|
|
1229
1270
|
- **Production-ready** - 9.8/10 code quality score
|
|
1230
1271
|
- **Type-safe** - Full Pydantic v2 and MyPy compliance
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "chuk-puzzles-gym"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "Multi-game puzzle gym for LLM training and benchmarking -
|
|
7
|
+
version = "0.10.1"
|
|
8
|
+
description = "Multi-game puzzle gym for LLM training and benchmarking - 30 constraint puzzles with synthetic data generation"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
11
11
|
license = {text = "MIT"}
|
|
@@ -235,6 +235,9 @@ async def _apply_hint(game: PuzzleGame, hint_data: tuple) -> MoveResult:
|
|
|
235
235
|
"nonogram",
|
|
236
236
|
"hidato",
|
|
237
237
|
"fillomino",
|
|
238
|
+
"skyscrapers",
|
|
239
|
+
"n-queens",
|
|
240
|
+
"numberlink",
|
|
238
241
|
]:
|
|
239
242
|
if len(hint_data) >= 3:
|
|
240
243
|
row, col, value = hint_data[0], hint_data[1], hint_data[2]
|
|
@@ -342,6 +345,24 @@ async def _apply_hint(game: PuzzleGame, hint_data: tuple) -> MoveResult:
|
|
|
342
345
|
direction = hint_data if isinstance(hint_data, str) else hint_data
|
|
343
346
|
return await game.validate_move(direction)
|
|
344
347
|
|
|
348
|
+
# Graph Coloring - hint is (node, color)
|
|
349
|
+
if game_name in ["graph coloring"]:
|
|
350
|
+
if len(hint_data) >= 2:
|
|
351
|
+
node, color = hint_data[0], hint_data[1]
|
|
352
|
+
return await game.validate_move(node, color)
|
|
353
|
+
|
|
354
|
+
# Cryptarithmetic - hint is (letter, digit)
|
|
355
|
+
if game_name in ["cryptarithmetic"]:
|
|
356
|
+
if len(hint_data) >= 2:
|
|
357
|
+
letter, digit = hint_data[0], hint_data[1]
|
|
358
|
+
return await game.validate_move(letter, digit)
|
|
359
|
+
|
|
360
|
+
# Rush Hour - hint is (vehicle_id, direction)
|
|
361
|
+
if game_name in ["rush hour"]:
|
|
362
|
+
if len(hint_data) >= 2:
|
|
363
|
+
vehicle_id, direction = hint_data[0], hint_data[1]
|
|
364
|
+
return await game.validate_move(vehicle_id, direction)
|
|
365
|
+
|
|
345
366
|
# Generic fallback - try validate_move with hint args as tuple
|
|
346
367
|
if isinstance(hint_data, tuple) and len(hint_data) >= 2:
|
|
347
368
|
return await game.validate_move(*hint_data)
|
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
from .binary import BinaryPuzzleGame
|
|
4
4
|
from .bridges import BridgesGame
|
|
5
|
+
from .cryptarithmetic import CryptarithmeticCommandHandler, CryptarithmeticGame
|
|
5
6
|
from .einstein import EinsteinGame
|
|
6
7
|
from .fillomino import FillominoGame
|
|
7
8
|
from .futoshiki import FutoshikiGame
|
|
9
|
+
from .graph_coloring import GraphColoringCommandHandler, GraphColoringGame
|
|
8
10
|
from .hidato import HidatoGame
|
|
9
11
|
from .hitori import HitoriGame
|
|
10
12
|
from .kakuro import KakuroGame
|
|
@@ -16,9 +18,13 @@ from .logic_grid import LogicGridGame
|
|
|
16
18
|
from .mastermind import MastermindGame
|
|
17
19
|
from .minesweeper import MinesweeperGame
|
|
18
20
|
from .nonogram import NonogramGame
|
|
21
|
+
from .nqueens import NQueensGame
|
|
22
|
+
from .numberlink import NumberlinkGame
|
|
19
23
|
from .nurikabe import NurikabeGame
|
|
24
|
+
from .rush_hour import RushHourCommandHandler, RushHourGame
|
|
20
25
|
from .scheduler import SchedulerGame
|
|
21
26
|
from .shikaku import ShikakuGame
|
|
27
|
+
from .skyscrapers import SkyscrapersGame
|
|
22
28
|
from .slitherlink import SlitherlinkGame
|
|
23
29
|
from .sokoban import SokobanGame
|
|
24
30
|
from .star_battle import StarBattleGame
|
|
@@ -57,11 +63,21 @@ AVAILABLE_GAMES = {
|
|
|
57
63
|
"nurikabe": NurikabeGame,
|
|
58
64
|
"einstein": EinsteinGame,
|
|
59
65
|
"minesweeper": MinesweeperGame,
|
|
66
|
+
# New Games
|
|
67
|
+
"skyscrapers": SkyscrapersGame,
|
|
68
|
+
"nqueens": NQueensGame,
|
|
69
|
+
"numberlink": NumberlinkGame,
|
|
70
|
+
"graph_coloring": GraphColoringGame,
|
|
71
|
+
"cryptarithmetic": CryptarithmeticGame,
|
|
72
|
+
"rush_hour": RushHourGame,
|
|
60
73
|
}
|
|
61
74
|
|
|
62
75
|
# Registry of game command handlers (games that have moved command handling out of server)
|
|
63
76
|
GAME_COMMAND_HANDLERS = {
|
|
64
77
|
"sudoku": SudokuCommandHandler,
|
|
78
|
+
"graph_coloring": GraphColoringCommandHandler,
|
|
79
|
+
"cryptarithmetic": CryptarithmeticCommandHandler,
|
|
80
|
+
"rush_hour": RushHourCommandHandler,
|
|
65
81
|
}
|
|
66
82
|
|
|
67
83
|
__all__ = [
|
|
@@ -89,6 +105,12 @@ __all__ = [
|
|
|
89
105
|
"NurikabeGame",
|
|
90
106
|
"EinsteinGame",
|
|
91
107
|
"MinesweeperGame",
|
|
108
|
+
"SkyscrapersGame",
|
|
109
|
+
"NQueensGame",
|
|
110
|
+
"NumberlinkGame",
|
|
111
|
+
"GraphColoringGame",
|
|
112
|
+
"CryptarithmeticGame",
|
|
113
|
+
"RushHourGame",
|
|
92
114
|
"AVAILABLE_GAMES",
|
|
93
115
|
"GAME_COMMAND_HANDLERS",
|
|
94
116
|
]
|
|
@@ -358,6 +358,8 @@ class BinaryPuzzleGame(PuzzleGame):
|
|
|
358
358
|
Returns:
|
|
359
359
|
Tuple of (hint_data, hint_message) or None if puzzle is complete
|
|
360
360
|
"""
|
|
361
|
+
if not self.can_use_hint():
|
|
362
|
+
return None
|
|
361
363
|
empty_cells = [(r, c) for r in range(self.size) for c in range(self.size) if self.grid[r][c] == -1]
|
|
362
364
|
if not empty_cells:
|
|
363
365
|
return None
|
|
@@ -415,6 +415,8 @@ class BridgesGame(PuzzleGame):
|
|
|
415
415
|
|
|
416
416
|
async def get_hint(self) -> tuple[Any, str] | None:
|
|
417
417
|
"""Get a hint for the next move."""
|
|
418
|
+
if not self.can_use_hint():
|
|
419
|
+
return None
|
|
418
420
|
# Find a bridge in the solution that's not yet placed correctly
|
|
419
421
|
for bridge_key, solution_count in self.solution.items():
|
|
420
422
|
current_count = self.bridges.get(bridge_key, 0)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"""Cryptarithmetic puzzle game."""
|
|
2
|
+
|
|
3
|
+
from .commands import CryptarithmeticCommandHandler
|
|
4
|
+
from .config import CryptarithmeticConfig
|
|
5
|
+
from .game import CryptarithmeticGame
|
|
6
|
+
|
|
7
|
+
__all__ = ["CryptarithmeticGame", "CryptarithmeticConfig", "CryptarithmeticCommandHandler"]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Command handler for Cryptarithmetic game."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from ...models import GameCommand, MoveResult
|
|
6
|
+
from .._base import CommandResult, GameCommandHandler
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from .game import CryptarithmeticGame
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class CryptarithmeticCommandHandler(GameCommandHandler):
|
|
13
|
+
"""Handles commands for Cryptarithmetic game."""
|
|
14
|
+
|
|
15
|
+
game: "CryptarithmeticGame"
|
|
16
|
+
|
|
17
|
+
@property
|
|
18
|
+
def supported_commands(self) -> set[GameCommand]:
|
|
19
|
+
"""Return the set of GameCommand enums this handler supports."""
|
|
20
|
+
return {GameCommand.ASSIGN, GameCommand.UNASSIGN}
|
|
21
|
+
|
|
22
|
+
async def handle_command(self, cmd: GameCommand, args: list[str]) -> CommandResult:
|
|
23
|
+
"""Handle a Cryptarithmetic command.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
cmd: The GameCommand enum value
|
|
27
|
+
args: List of string arguments (already split from input)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
CommandResult with the move result and display flags
|
|
31
|
+
"""
|
|
32
|
+
if cmd == GameCommand.ASSIGN:
|
|
33
|
+
return await self._handle_assign(args)
|
|
34
|
+
elif cmd == GameCommand.UNASSIGN:
|
|
35
|
+
return await self._handle_unassign(args)
|
|
36
|
+
else:
|
|
37
|
+
return self.error_result(f"Unknown command: {cmd}")
|
|
38
|
+
|
|
39
|
+
async def _handle_assign(self, args: list[str]) -> CommandResult:
|
|
40
|
+
"""Handle the ASSIGN command: assign <letter> <digit>."""
|
|
41
|
+
if len(args) != 2:
|
|
42
|
+
return CommandResult(
|
|
43
|
+
result=MoveResult(success=False, message="Usage: assign <letter> <digit>"),
|
|
44
|
+
should_display=False,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
letter = args[0].upper()
|
|
48
|
+
digit = self.parse_int(args[1], "digit")
|
|
49
|
+
|
|
50
|
+
if digit is None:
|
|
51
|
+
return self.error_result("Digit must be an integer (0-9).")
|
|
52
|
+
|
|
53
|
+
result = await self.game.validate_move(letter, digit)
|
|
54
|
+
|
|
55
|
+
return CommandResult(
|
|
56
|
+
result=result,
|
|
57
|
+
should_display=result.success,
|
|
58
|
+
is_game_over=result.success and self.game.is_complete(),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
async def _handle_unassign(self, args: list[str]) -> CommandResult:
|
|
62
|
+
"""Handle the UNASSIGN command: unassign <letter>."""
|
|
63
|
+
if len(args) != 1:
|
|
64
|
+
return CommandResult(
|
|
65
|
+
result=MoveResult(success=False, message="Usage: unassign <letter>"),
|
|
66
|
+
should_display=False,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
letter = args[0].upper()
|
|
70
|
+
result = await self.game.validate_move(letter, -1)
|
|
71
|
+
|
|
72
|
+
return CommandResult(
|
|
73
|
+
result=result,
|
|
74
|
+
should_display=result.success,
|
|
75
|
+
)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""Configuration for Cryptarithmetic puzzle game."""
|
|
2
|
+
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
from ...models import DifficultyLevel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CryptarithmeticConfig(BaseModel):
|
|
9
|
+
"""Configuration for a Cryptarithmetic puzzle."""
|
|
10
|
+
|
|
11
|
+
difficulty: DifficultyLevel = Field(default=DifficultyLevel.EASY)
|
|
12
|
+
max_word_length: int = Field(ge=2, le=6, description="Maximum word length")
|
|
13
|
+
pre_assigned: int = Field(ge=0, description="Number of pre-assigned letter-digit pairs")
|
|
14
|
+
|
|
15
|
+
@classmethod
|
|
16
|
+
def from_difficulty(cls, difficulty: DifficultyLevel) -> "CryptarithmeticConfig":
|
|
17
|
+
"""Create config from difficulty level."""
|
|
18
|
+
config_map = {
|
|
19
|
+
DifficultyLevel.EASY: {"max_word_length": 3, "pre_assigned": 3},
|
|
20
|
+
DifficultyLevel.MEDIUM: {"max_word_length": 4, "pre_assigned": 2},
|
|
21
|
+
DifficultyLevel.HARD: {"max_word_length": 5, "pre_assigned": 0},
|
|
22
|
+
}
|
|
23
|
+
return cls(difficulty=difficulty, **config_map[difficulty])
|