chuk-puzzles-gym 0.10__py3-none-any.whl → 0.10.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. chuk_puzzles_gym/games/binary/game.py +2 -0
  2. chuk_puzzles_gym/games/bridges/game.py +2 -0
  3. chuk_puzzles_gym/games/cryptarithmetic/game.py +5 -0
  4. chuk_puzzles_gym/games/einstein/game.py +2 -0
  5. chuk_puzzles_gym/games/fillomino/game.py +2 -0
  6. chuk_puzzles_gym/games/futoshiki/game.py +2 -0
  7. chuk_puzzles_gym/games/graph_coloring/commands.py +20 -3
  8. chuk_puzzles_gym/games/graph_coloring/game.py +8 -1
  9. chuk_puzzles_gym/games/hidato/game.py +2 -0
  10. chuk_puzzles_gym/games/hitori/game.py +2 -0
  11. chuk_puzzles_gym/games/kakuro/game.py +2 -0
  12. chuk_puzzles_gym/games/kenken/game.py +2 -0
  13. chuk_puzzles_gym/games/killer_sudoku/game.py +2 -0
  14. chuk_puzzles_gym/games/knapsack/game.py +2 -0
  15. chuk_puzzles_gym/games/lights_out/game.py +2 -0
  16. chuk_puzzles_gym/games/logic_grid/game.py +2 -0
  17. chuk_puzzles_gym/games/mastermind/game.py +2 -0
  18. chuk_puzzles_gym/games/minesweeper/game.py +2 -0
  19. chuk_puzzles_gym/games/nonogram/game.py +2 -0
  20. chuk_puzzles_gym/games/nqueens/game.py +5 -0
  21. chuk_puzzles_gym/games/numberlink/game.py +6 -0
  22. chuk_puzzles_gym/games/nurikabe/game.py +2 -0
  23. chuk_puzzles_gym/games/rush_hour/game.py +4 -0
  24. chuk_puzzles_gym/games/scheduler/game.py +2 -0
  25. chuk_puzzles_gym/games/shikaku/game.py +2 -0
  26. chuk_puzzles_gym/games/skyscrapers/game.py +5 -0
  27. chuk_puzzles_gym/games/slitherlink/game.py +2 -0
  28. chuk_puzzles_gym/games/sokoban/game.py +2 -0
  29. chuk_puzzles_gym/games/star_battle/game.py +2 -0
  30. chuk_puzzles_gym/games/sudoku/game.py +2 -0
  31. chuk_puzzles_gym/games/tents/game.py +2 -0
  32. chuk_puzzles_gym/server.py +17 -69
  33. {chuk_puzzles_gym-0.10.dist-info → chuk_puzzles_gym-0.10.1.dist-info}/METADATA +1 -1
  34. {chuk_puzzles_gym-0.10.dist-info → chuk_puzzles_gym-0.10.1.dist-info}/RECORD +37 -37
  35. {chuk_puzzles_gym-0.10.dist-info → chuk_puzzles_gym-0.10.1.dist-info}/WHEEL +0 -0
  36. {chuk_puzzles_gym-0.10.dist-info → chuk_puzzles_gym-0.10.1.dist-info}/entry_points.txt +0 -0
  37. {chuk_puzzles_gym-0.10.dist-info → chuk_puzzles_gym-0.10.1.dist-info}/top_level.txt +0 -0
@@ -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)
@@ -362,6 +362,11 @@ class CryptarithmeticGame(PuzzleGame):
362
362
 
363
363
  return "\n".join(lines)
364
364
 
365
+ def get_stats(self) -> str:
366
+ """Get current game statistics."""
367
+ assigned = sum(1 for v in self.player_mapping.values() if v is not None)
368
+ return f"Moves: {self.moves_made} | Assigned: {assigned}/{len(self.letters)} | Seed: {self.seed}"
369
+
365
370
  def get_rules(self) -> str:
366
371
  return (
367
372
  "CRYPTARITHMETIC\n"
@@ -281,6 +281,8 @@ class EinsteinGame(PuzzleGame):
281
281
  Returns:
282
282
  Tuple of (hint_data, hint_message) or None
283
283
  """
284
+ if not self.can_use_hint():
285
+ return None
284
286
  # Find first unassigned attribute in solution
285
287
  for i in range(self.num_houses):
286
288
  for attr in ATTRIBUTES:
@@ -435,6 +435,8 @@ class FillominoGame(PuzzleGame):
435
435
  Returns:
436
436
  Tuple of (hint_data, hint_message) or None if puzzle is complete
437
437
  """
438
+ if not self.can_use_hint():
439
+ return None
438
440
  # Find an empty cell
439
441
  for r in range(self.size):
440
442
  for c in range(self.size):
@@ -288,6 +288,8 @@ class FutoshikiGame(PuzzleGame):
288
288
  Returns:
289
289
  Tuple of (hint_data, hint_message) or None if puzzle is complete
290
290
  """
291
+ if not self.can_use_hint():
292
+ return None
291
293
  empty_cells = [(r, c) for r in range(self.size) for c in range(self.size) if self.grid[r][c] == 0]
292
294
  if not empty_cells:
293
295
  return None
@@ -4,10 +4,14 @@ from typing import TYPE_CHECKING
4
4
 
5
5
  from ...models import GameCommand, MoveResult
6
6
  from .._base import CommandResult, GameCommandHandler
7
+ from .game import COLOR_NAMES
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from .game import GraphColoringGame
10
11
 
12
+ # Build a lookup from lowercase color name to color number
13
+ _COLOR_NAME_TO_NUM = {name.lower(): i + 1 for i, name in enumerate(COLOR_NAMES)}
14
+
11
15
 
12
16
  class GraphColoringCommandHandler(GameCommandHandler):
13
17
  """Handles commands for Graph Coloring game."""
@@ -36,6 +40,16 @@ class GraphColoringCommandHandler(GameCommandHandler):
36
40
  else:
37
41
  return self.error_result(f"Unknown command: {cmd}")
38
42
 
43
+ def _parse_color(self, value: str) -> int | None:
44
+ """Parse a color argument as either an integer or a color name."""
45
+ # Try integer first
46
+ try:
47
+ return int(value)
48
+ except ValueError:
49
+ pass
50
+ # Try color name lookup
51
+ return _COLOR_NAME_TO_NUM.get(value.lower())
52
+
39
53
  async def _handle_place(self, args: list[str]) -> CommandResult:
40
54
  """Handle the PLACE command: place <node> <color>."""
41
55
  if len(args) != 2:
@@ -45,10 +59,13 @@ class GraphColoringCommandHandler(GameCommandHandler):
45
59
  )
46
60
 
47
61
  node = self.parse_int(args[0], "node")
48
- color = self.parse_int(args[1], "color")
62
+ color = self._parse_color(args[1])
49
63
 
50
- if node is None or color is None:
51
- return self.error_result("Node and color must be integers.")
64
+ if node is None:
65
+ return self.error_result("Node must be an integer.")
66
+ if color is None:
67
+ valid = ", ".join(f"{i + 1}={COLOR_NAMES[i]}" for i in range(self.game.num_colors))
68
+ return self.error_result(f"Invalid color. Use a number or name: {valid}")
52
69
 
53
70
  result = await self.game.validate_move(node, color)
54
71
 
@@ -289,6 +289,11 @@ class GraphColoringGame(PuzzleGame):
289
289
 
290
290
  return "\n".join(lines)
291
291
 
292
+ def get_stats(self) -> str:
293
+ """Get current game statistics."""
294
+ colored = sum(1 for n in range(1, self.num_nodes + 1) if self.coloring.get(n, 0) > 0)
295
+ return f"Moves: {self.moves_made} | Colored: {colored}/{self.num_nodes} | Edges: {len(self.edges)} | Seed: {self.seed}"
296
+
292
297
  def get_rules(self) -> str:
293
298
  return (
294
299
  f"GRAPH COLORING ({self.num_nodes} nodes, {self.num_colors} colors)\n"
@@ -298,9 +303,11 @@ class GraphColoringGame(PuzzleGame):
298
303
  )
299
304
 
300
305
  def get_commands(self) -> str:
306
+ color_map = ", ".join(f"{i + 1}={COLOR_NAMES[i]}" for i in range(self.num_colors))
301
307
  return (
302
308
  "Commands:\n"
303
- f" place <node> <color> - Color a node (1-{self.num_colors})\n"
309
+ f" place <node> <color> - Color a node (number or name)\n"
310
+ f" Colors: {color_map}\n"
304
311
  " clear <node> - Remove color from a node\n"
305
312
  " hint - Get a hint\n"
306
313
  " check - Check if solved\n"
@@ -302,6 +302,8 @@ class HidatoGame(PuzzleGame):
302
302
  Returns:
303
303
  Tuple of (hint_data, hint_message) or None if puzzle is complete
304
304
  """
305
+ if not self.can_use_hint():
306
+ return None
305
307
  # Find an empty cell
306
308
  empty_cells = [(r, c) for r in range(self.size) for c in range(self.size) if self.grid[r][c] == 0]
307
309
  if not empty_cells:
@@ -395,6 +395,8 @@ class HitoriGame(PuzzleGame):
395
395
 
396
396
  async def get_hint(self) -> tuple[Any, str] | None:
397
397
  """Get a hint for the next move."""
398
+ if not self.can_use_hint():
399
+ return None
398
400
  # Find a cell that should be shaded but isn't, or vice versa
399
401
  for r in range(self.size):
400
402
  for c in range(self.size):
@@ -311,6 +311,8 @@ class KakuroGame(PuzzleGame):
311
311
  Returns:
312
312
  Tuple of (hint_data, hint_message) or None if puzzle is complete
313
313
  """
314
+ if not self.can_use_hint():
315
+ return None
314
316
  empty_cells = [
315
317
  (r, c)
316
318
  for r in range(self.size)
@@ -366,6 +366,8 @@ class KenKenGame(PuzzleGame):
366
366
  Returns:
367
367
  Tuple of (hint_data, hint_message) or None if puzzle is complete
368
368
  """
369
+ if not self.can_use_hint():
370
+ return None
369
371
  empty_cells = [(r, c) for r in range(self.size) for c in range(self.size) if self.grid[r][c] == 0]
370
372
  if not empty_cells:
371
373
  return None
@@ -395,6 +395,8 @@ class KillerSudokuGame(PuzzleGame):
395
395
  Returns:
396
396
  Tuple of (hint_data, hint_message) or None if puzzle is complete
397
397
  """
398
+ if not self.can_use_hint():
399
+ return None
398
400
  empty_cells = [(r, c) for r in range(9) for c in range(9) if self.grid[r][c] == 0]
399
401
  if not empty_cells:
400
402
  return None
@@ -255,6 +255,8 @@ class KnapsackGame(PuzzleGame):
255
255
  Returns:
256
256
  Tuple of (hint_data, hint_message) or None
257
257
  """
258
+ if not self.can_use_hint():
259
+ return None
258
260
  # Suggest selecting an item that's in the optimal solution but not selected
259
261
  for i in range(len(self.items)):
260
262
  if self.optimal_selection[i] and not self.selection[i]:
@@ -173,6 +173,8 @@ class LightsOutGame(PuzzleGame):
173
173
  Returns:
174
174
  Tuple of (hint_data, hint_message) or None if puzzle is complete
175
175
  """
176
+ if not self.can_use_hint():
177
+ return None
176
178
  # Find a cell in the solution that should be pressed
177
179
  for row in range(self.size):
178
180
  for col in range(self.size):
@@ -235,6 +235,8 @@ class LogicGridGame(PuzzleGame):
235
235
  Returns:
236
236
  Tuple of (hint_data, hint_message) or None if puzzle is complete
237
237
  """
238
+ if not self.can_use_hint():
239
+ return None
238
240
  # Find a connection that hasn't been marked
239
241
  for person in self.categories.person:
240
242
  attrs = self.solution[person]
@@ -211,6 +211,8 @@ class MastermindGame(PuzzleGame):
211
211
  Returns:
212
212
  Tuple of (hint_data, hint_message) or None if no hints available
213
213
  """
214
+ if not self.can_use_hint():
215
+ return None
214
216
  if self.is_complete():
215
217
  return None
216
218
 
@@ -310,6 +310,8 @@ class MinesweeperGame(PuzzleGame):
310
310
  Returns:
311
311
  Tuple of (hint_data, hint_message) or None
312
312
  """
313
+ if not self.can_use_hint():
314
+ return None
313
315
  if self.game_over:
314
316
  return None
315
317
 
@@ -192,6 +192,8 @@ class NonogramGame(PuzzleGame):
192
192
  Returns:
193
193
  Tuple of (hint_data, hint_message) or None if puzzle is complete
194
194
  """
195
+ if not self.can_use_hint():
196
+ return None
195
197
  unknown_cells = [(r, c) for r in range(self.size) for c in range(self.size) if self.grid[r][c] == -1]
196
198
  if not unknown_cells:
197
199
  return None
@@ -296,6 +296,11 @@ class NQueensGame(PuzzleGame):
296
296
 
297
297
  return "\n".join(lines)
298
298
 
299
+ def get_stats(self) -> str:
300
+ """Get current game statistics."""
301
+ placed = sum(1 for r in range(self.size) for c in range(self.size) if self.grid[r][c] == 1)
302
+ return f"Moves: {self.moves_made} | Queens: {placed}/{self.size} | Board: {self.size}x{self.size} | Seed: {self.seed}"
303
+
299
304
  def get_rules(self) -> str:
300
305
  return (
301
306
  f"N-QUEENS ({self.size}x{self.size})\n"
@@ -317,6 +317,12 @@ class NumberlinkGame(PuzzleGame):
317
317
 
318
318
  return "\n".join(lines)
319
319
 
320
+ def get_stats(self) -> str:
321
+ """Get current game statistics."""
322
+ filled = sum(1 for r in range(self.size) for c in range(self.size) if self.grid[r][c] != 0)
323
+ total = self.size * self.size
324
+ return f"Moves: {self.moves_made} | Filled: {filled}/{total} | Pairs: {self.num_pairs} | Seed: {self.seed}"
325
+
320
326
  def get_rules(self) -> str:
321
327
  return (
322
328
  f"NUMBERLINK ({self.size}x{self.size}, {self.num_pairs} pairs)\n"
@@ -479,6 +479,8 @@ class NurikabeGame(PuzzleGame):
479
479
  Returns:
480
480
  Tuple of (hint_data, hint_message) or None
481
481
  """
482
+ if not self.can_use_hint():
483
+ return None
482
484
  # Find a cell that differs from solution
483
485
  for row in range(self.size):
484
486
  for col in range(self.size):
@@ -454,6 +454,10 @@ class RushHourGame(PuzzleGame):
454
454
 
455
455
  return "\n".join(lines)
456
456
 
457
+ def get_stats(self) -> str:
458
+ """Get current game statistics."""
459
+ return f"Moves: {self.moves_made} | Vehicles: {len(self.vehicles)} | Grid: {self.size}x{self.size} | Seed: {self.seed}"
460
+
457
461
  def get_rules(self) -> str:
458
462
  return (
459
463
  f"RUSH HOUR ({self.size}x{self.size})\n"
@@ -316,6 +316,8 @@ class SchedulerGame(PuzzleGame):
316
316
  Returns:
317
317
  Tuple of (hint_data, hint_message) or None
318
318
  """
319
+ if not self.can_use_hint():
320
+ return None
319
321
  # Find an unscheduled task that's in the optimal solution
320
322
  for task_id in range(self.num_tasks):
321
323
  if task_id not in self.schedule and task_id in self.optimal_schedule:
@@ -327,6 +327,8 @@ class ShikakuGame(PuzzleGame):
327
327
 
328
328
  async def get_hint(self) -> tuple[Any, str] | None:
329
329
  """Get a hint for the next move."""
330
+ if not self.can_use_hint():
331
+ return None
330
332
  # Find a rectangle from the solution that hasn't been placed yet
331
333
  solution_rects: dict[int, list[tuple[int, int]]] = {}
332
334
  for r in range(self.size):
@@ -255,6 +255,11 @@ class SkyscrapersGame(PuzzleGame):
255
255
 
256
256
  return "\n".join(lines)
257
257
 
258
+ def get_stats(self) -> str:
259
+ """Get current game statistics."""
260
+ empty = sum(1 for r in range(self.size) for c in range(self.size) if self.grid[r][c] == 0)
261
+ return f"Moves: {self.moves_made} | Empty cells: {empty} | Grid: {self.size}x{self.size} | Seed: {self.seed}"
262
+
258
263
  def get_rules(self) -> str:
259
264
  return (
260
265
  f"SKYSCRAPERS ({self.size}x{self.size})\n"
@@ -272,6 +272,8 @@ class SlitherlinkGame(PuzzleGame):
272
272
  Returns:
273
273
  Tuple of (hint_data, hint_message) or None
274
274
  """
275
+ if not self.can_use_hint():
276
+ return None
275
277
  # Find an edge that's in the solution but not set by player
276
278
  for row in range(self.size + 1):
277
279
  for col in range(self.size):
@@ -499,6 +499,8 @@ class SokobanGame(PuzzleGame):
499
499
  Returns:
500
500
  Tuple of (hint_data, hint_message) or None
501
501
  """
502
+ if not self.can_use_hint():
503
+ return None
502
504
  if self.is_complete():
503
505
  return None
504
506
 
@@ -301,6 +301,8 @@ class StarBattleGame(PuzzleGame):
301
301
  Returns:
302
302
  Tuple of (hint_data, hint_message) or None if puzzle is complete
303
303
  """
304
+ if not self.can_use_hint():
305
+ return None
304
306
  # Find a star location from solution that hasn't been placed
305
307
  for r in range(self.size):
306
308
  for c in range(self.size):
@@ -249,6 +249,8 @@ class SudokuGame(PuzzleGame):
249
249
  Returns:
250
250
  Tuple of (hint_data, hint_message) or None if puzzle is complete
251
251
  """
252
+ if not self.can_use_hint():
253
+ return None
252
254
  empty_cells = [(r, c) for r in range(9) for c in range(9) if self.grid[r][c] == 0]
253
255
  if not empty_cells:
254
256
  return None
@@ -326,6 +326,8 @@ class TentsGame(PuzzleGame):
326
326
  Returns:
327
327
  Tuple of (hint_data, hint_message) or None if puzzle is complete
328
328
  """
329
+ if not self.can_use_hint():
330
+ return None
329
331
  # Find a tent location from solution that hasn't been placed
330
332
  for r in range(self.size):
331
333
  for c in range(self.size):
@@ -599,10 +599,6 @@ class ArcadeHandler(TelnetHandler):
599
599
  if self.game_handler and cmd_enum in self.game_handler.supported_commands:
600
600
  result = await self.game_handler.handle_command(cmd_enum, parts[1:])
601
601
 
602
- # Track invalid moves
603
- if not result.result.success:
604
- self.current_game.invalid_moves += 1
605
-
606
602
  # Send result based on output mode
607
603
  code = "OK" if result.result.success else "INVALID"
608
604
  await self.send_result(result.result.success, result.result.message, code)
@@ -626,9 +622,7 @@ class ArcadeHandler(TelnetHandler):
626
622
  num = int(parts[3])
627
623
 
628
624
  result = await self.current_game.validate_move(row, col, num)
629
-
630
- if not result.success:
631
- self.current_game.invalid_moves += 1
625
+ self.current_game.record_move((row, col), result.success)
632
626
 
633
627
  await self.send_result(result.success, result.message, "PLACED" if result.success else "INVALID_MOVE")
634
628
 
@@ -639,7 +633,6 @@ class ArcadeHandler(TelnetHandler):
639
633
  await self.send_game_complete()
640
634
 
641
635
  except ValueError:
642
- self.current_game.invalid_moves += 1
643
636
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
644
637
  return
645
638
 
@@ -653,9 +646,7 @@ class ArcadeHandler(TelnetHandler):
653
646
  col = int(parts[2])
654
647
 
655
648
  result = await self.current_game.validate_move(row, col, 0)
656
-
657
- if not result.success:
658
- self.current_game.invalid_moves += 1
649
+ self.current_game.record_move((row, col), result.success)
659
650
 
660
651
  await self.send_result(result.success, result.message, "CLEARED" if result.success else "INVALID_CLEAR")
661
652
 
@@ -663,7 +654,6 @@ class ArcadeHandler(TelnetHandler):
663
654
  await self.display_puzzle()
664
655
 
665
656
  except ValueError:
666
- self.current_game.invalid_moves += 1
667
657
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
668
658
  return
669
659
 
@@ -693,9 +683,7 @@ class ArcadeHandler(TelnetHandler):
693
683
  col = int(parts[2])
694
684
 
695
685
  result = await self.current_game.validate_move(row, col)
696
-
697
- if not result.success:
698
- self.current_game.invalid_moves += 1
686
+ self.current_game.record_move((row, col), result.success)
699
687
 
700
688
  await self.send_result(result.success, result.message, "PRESSED" if result.success else "INVALID_PRESS")
701
689
 
@@ -706,7 +694,6 @@ class ArcadeHandler(TelnetHandler):
706
694
  await self.send_game_complete()
707
695
 
708
696
  except ValueError:
709
- self.current_game.invalid_moves += 1
710
697
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
711
698
  return
712
699
 
@@ -718,9 +705,7 @@ class ArcadeHandler(TelnetHandler):
718
705
 
719
706
  cat1, val1, cat2, val2 = parts[1], parts[2], parts[3], parts[4]
720
707
  result = await self.current_game.validate_move(cat1, val1, cat2, val2, True)
721
-
722
- if not result.success:
723
- self.current_game.invalid_moves += 1
708
+ self.current_game.record_move((cat1, val1, cat2, val2), result.success)
724
709
 
725
710
  await self.send_result(result.success, result.message, "CONNECTED" if result.success else "INVALID_CONNECT")
726
711
  if result.success:
@@ -736,9 +721,7 @@ class ArcadeHandler(TelnetHandler):
736
721
 
737
722
  cat1, val1, cat2, val2 = parts[1], parts[2], parts[3], parts[4]
738
723
  result = await self.current_game.validate_move(cat1, val1, cat2, val2, False)
739
-
740
- if not result.success:
741
- self.current_game.invalid_moves += 1
724
+ self.current_game.record_move((cat1, val1, cat2, val2), result.success)
742
725
 
743
726
  await self.send_result(result.success, result.message, "EXCLUDED" if result.success else "INVALID_EXCLUDE")
744
727
  if result.success:
@@ -758,9 +741,7 @@ class ArcadeHandler(TelnetHandler):
758
741
  col = int(parts[2])
759
742
 
760
743
  result = await self.current_game.validate_move("reveal", row, col)
761
-
762
- if not result.success:
763
- self.current_game.invalid_moves += 1
744
+ self.current_game.record_move((row, col), result.success)
764
745
 
765
746
  await self.send_result(
766
747
  result.success, result.message, "REVEALED" if result.success else "INVALID_REVEAL"
@@ -783,7 +764,6 @@ class ArcadeHandler(TelnetHandler):
783
764
  await self.send_line("=" * 50 + "\n")
784
765
 
785
766
  except ValueError:
786
- self.current_game.invalid_moves += 1
787
767
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
788
768
  return
789
769
 
@@ -797,9 +777,7 @@ class ArcadeHandler(TelnetHandler):
797
777
  col = int(parts[2])
798
778
 
799
779
  result = await self.current_game.validate_move("flag", row, col)
800
-
801
- if not result.success:
802
- self.current_game.invalid_moves += 1
780
+ self.current_game.record_move((row, col), result.success)
803
781
 
804
782
  await self.send_result(result.success, result.message, "FLAGGED" if result.success else "INVALID_FLAG")
805
783
 
@@ -807,7 +785,6 @@ class ArcadeHandler(TelnetHandler):
807
785
  await self.display_puzzle()
808
786
 
809
787
  except ValueError:
810
- self.current_game.invalid_moves += 1
811
788
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
812
789
  return
813
790
 
@@ -824,9 +801,7 @@ class ArcadeHandler(TelnetHandler):
824
801
  state = int(parts[4])
825
802
 
826
803
  result = await self.current_game.validate_move(edge_type, row, col, state)
827
-
828
- if not result.success:
829
- self.current_game.invalid_moves += 1
804
+ self.current_game.record_move((edge_type, row, col), result.success)
830
805
 
831
806
  await self.send_result(result.success, result.message, "SET" if result.success else "INVALID_SET")
832
807
 
@@ -837,7 +812,6 @@ class ArcadeHandler(TelnetHandler):
837
812
  await self.send_game_complete()
838
813
 
839
814
  except ValueError:
840
- self.current_game.invalid_moves += 1
841
815
  await self.send_result(False, "Invalid input. Use numbers only for row, col, state.", "PARSE_ERROR")
842
816
  return
843
817
 
@@ -851,9 +825,7 @@ class ArcadeHandler(TelnetHandler):
851
825
  guess = [int(p) for p in parts[1:]]
852
826
 
853
827
  result = await self.current_game.validate_move(*guess)
854
-
855
- if not result.success:
856
- self.current_game.invalid_moves += 1
828
+ self.current_game.record_move(tuple(guess), result.success)
857
829
 
858
830
  await self.send_result(result.success, result.message, "GUESSED" if result.success else "INVALID_GUESS")
859
831
 
@@ -874,7 +846,6 @@ class ArcadeHandler(TelnetHandler):
874
846
  await self.send_line("=" * 50 + "\n")
875
847
 
876
848
  except ValueError:
877
- self.current_game.invalid_moves += 1
878
849
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
879
850
  return
880
851
 
@@ -888,9 +859,7 @@ class ArcadeHandler(TelnetHandler):
888
859
  item_index = int(parts[1])
889
860
 
890
861
  result = await self.current_game.validate_move("select", item_index)
891
-
892
- if not result.success:
893
- self.current_game.invalid_moves += 1
862
+ self.current_game.record_move((item_index,), result.success)
894
863
 
895
864
  await self.send_result(
896
865
  result.success, result.message, "SELECTED" if result.success else "INVALID_SELECT"
@@ -900,7 +869,6 @@ class ArcadeHandler(TelnetHandler):
900
869
  await self.display_puzzle()
901
870
 
902
871
  except ValueError:
903
- self.current_game.invalid_moves += 1
904
872
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
905
873
  return
906
874
 
@@ -913,9 +881,7 @@ class ArcadeHandler(TelnetHandler):
913
881
  item_index = int(parts[1])
914
882
 
915
883
  result = await self.current_game.validate_move("deselect", item_index)
916
-
917
- if not result.success:
918
- self.current_game.invalid_moves += 1
884
+ self.current_game.record_move((item_index,), result.success)
919
885
 
920
886
  await self.send_result(
921
887
  result.success, result.message, "DESELECTED" if result.success else "INVALID_DESELECT"
@@ -925,7 +891,6 @@ class ArcadeHandler(TelnetHandler):
925
891
  await self.display_puzzle()
926
892
 
927
893
  except ValueError:
928
- self.current_game.invalid_moves += 1
929
894
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
930
895
  return
931
896
 
@@ -941,9 +906,7 @@ class ArcadeHandler(TelnetHandler):
941
906
  color = parts[3].lower()
942
907
 
943
908
  result = await self.current_game.validate_move(row, col, color)
944
-
945
- if not result.success:
946
- self.current_game.invalid_moves += 1
909
+ self.current_game.record_move((row, col), result.success)
947
910
 
948
911
  await self.send_result(result.success, result.message, "MARKED" if result.success else "INVALID_MARK")
949
912
 
@@ -954,7 +917,6 @@ class ArcadeHandler(TelnetHandler):
954
917
  await self.send_game_complete()
955
918
 
956
919
  except ValueError:
957
- self.current_game.invalid_moves += 1
958
920
  await self.send_result(False, "Invalid input. Row and col must be numbers.", "PARSE_ERROR")
959
921
  return
960
922
 
@@ -969,9 +931,7 @@ class ArcadeHandler(TelnetHandler):
969
931
  col = int(parts[2])
970
932
 
971
933
  result = await self.current_game.validate_move(row, col, "shade")
972
-
973
- if not result.success:
974
- self.current_game.invalid_moves += 1
934
+ self.current_game.record_move((row, col), result.success)
975
935
 
976
936
  await self.send_result(result.success, result.message, "SHADED" if result.success else "INVALID_SHADE")
977
937
 
@@ -982,7 +942,6 @@ class ArcadeHandler(TelnetHandler):
982
942
  await self.send_game_complete()
983
943
 
984
944
  except ValueError:
985
- self.current_game.invalid_moves += 1
986
945
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
987
946
  return
988
947
 
@@ -1000,9 +959,7 @@ class ArcadeHandler(TelnetHandler):
1000
959
  count = int(parts[5])
1001
960
 
1002
961
  result = await self.current_game.validate_move(r1, c1, r2, c2, count)
1003
-
1004
- if not result.success:
1005
- self.current_game.invalid_moves += 1
962
+ self.current_game.record_move((r1, c1, r2, c2), result.success)
1006
963
 
1007
964
  await self.send_result(
1008
965
  result.success, result.message, "BRIDGED" if result.success else "INVALID_BRIDGE"
@@ -1015,7 +972,6 @@ class ArcadeHandler(TelnetHandler):
1015
972
  await self.send_game_complete()
1016
973
 
1017
974
  except ValueError:
1018
- self.current_game.invalid_moves += 1
1019
975
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
1020
976
  return
1021
977
 
@@ -1028,9 +984,7 @@ class ArcadeHandler(TelnetHandler):
1028
984
  direction = parts[1].lower()
1029
985
 
1030
986
  result = await self.current_game.validate_move(direction)
1031
-
1032
- if not result.success:
1033
- self.current_game.invalid_moves += 1
987
+ self.current_game.record_move((direction,), result.success)
1034
988
 
1035
989
  await self.send_result(result.success, result.message, "MOVED" if result.success else "INVALID_MOVE")
1036
990
 
@@ -1054,9 +1008,7 @@ class ArcadeHandler(TelnetHandler):
1054
1008
  start_time = int(parts[3])
1055
1009
 
1056
1010
  result = await self.current_game.validate_move(task_id, worker_id, start_time)
1057
-
1058
- if not result.success:
1059
- self.current_game.invalid_moves += 1
1011
+ self.current_game.record_move((task_id,), result.success)
1060
1012
 
1061
1013
  await self.send_result(
1062
1014
  result.success, result.message, "ASSIGNED" if result.success else "INVALID_ASSIGN"
@@ -1068,7 +1020,6 @@ class ArcadeHandler(TelnetHandler):
1068
1020
  await self.send_game_complete()
1069
1021
 
1070
1022
  except ValueError:
1071
- self.current_game.invalid_moves += 1
1072
1023
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
1073
1024
  return
1074
1025
 
@@ -1081,9 +1032,7 @@ class ArcadeHandler(TelnetHandler):
1081
1032
  task_id = int(parts[1])
1082
1033
 
1083
1034
  result = await self.current_game.validate_move(task_id, 0, -1)
1084
-
1085
- if not result.success:
1086
- self.current_game.invalid_moves += 1
1035
+ self.current_game.record_move((task_id,), result.success)
1087
1036
 
1088
1037
  await self.send_result(
1089
1038
  result.success, result.message, "UNASSIGNED" if result.success else "INVALID_UNASSIGN"
@@ -1093,7 +1042,6 @@ class ArcadeHandler(TelnetHandler):
1093
1042
  await self.display_puzzle()
1094
1043
 
1095
1044
  except ValueError:
1096
- self.current_game.invalid_moves += 1
1097
1045
  await self.send_result(False, "Invalid input. Use numbers only.", "PARSE_ERROR")
1098
1046
  return
1099
1047
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chuk-puzzles-gym
3
- Version: 0.10
3
+ Version: 0.10.1
4
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
@@ -2,7 +2,7 @@ chuk_puzzles_gym/__init__.py,sha256=zh2sc6QFKrtAmMLee7vlHgXuOBoB5CjSldlKFjZTVVE,
2
2
  chuk_puzzles_gym/constants.py,sha256=58pKdvwoaB4PF1AK4b7mLNf_Y_YFyFassd1hYH1IUNE,280
3
3
  chuk_puzzles_gym/eval.py,sha256=jWjfQ4OaBNY2vDwcRxw1-MC27VorLNUMfRW-lQpK3Rs,26415
4
4
  chuk_puzzles_gym/gym_env.py,sha256=qoQZFz2Dnbl3QjTsDNHAxAx1qomU8paXVlH-SDcwlZI,17288
5
- chuk_puzzles_gym/server.py,sha256=DARr5JeFHy9w4yE2n4WshHsBN08WIZU4x1_A9rM-UQw,47143
5
+ chuk_puzzles_gym/server.py,sha256=QnG48mXd8AKDVFUULIwLWidqDvcsCkayxbvo7h_EKBg,45947
6
6
  chuk_puzzles_gym/export/__init__.py,sha256=TTXBRR5CBBCL04r1iXMzxib9oOIDTC4npxy2_L1xc2A,366
7
7
  chuk_puzzles_gym/export/dataset.py,sha256=dZMz9m4JwpZZSigvaJjIpGKIoxUWB01gXoyNCZ4o17o,10998
8
8
  chuk_puzzles_gym/games/__init__.py,sha256=zByuxje5uVWQ4wBoGHUooHkAg5cgCljrCCXkyOLxLzo,3403
@@ -11,113 +11,113 @@ chuk_puzzles_gym/games/_base/commands.py,sha256=tY0kxk08D8nPr_C_awo8qDUhkL6EHA59
11
11
  chuk_puzzles_gym/games/_base/game.py,sha256=-YPJOgWsb4YVz8tS3cXJYd-y-1Tyx7eh8vs3tZEXcEA,11240
12
12
  chuk_puzzles_gym/games/binary/__init__.py,sha256=Pphgj0kcvHUgkM0Mq89GsWPt-Bg6DobDLi7cqliOywk,156
13
13
  chuk_puzzles_gym/games/binary/config.py,sha256=Iw8Wax1856aqaz1KvDC69Qou6z8gxIWr5rSAI0MGnWg,812
14
- chuk_puzzles_gym/games/binary/game.py,sha256=ohJnohzDED-IKLzcYnIUG-hZjyawWJr2GTvmFO-1j20,16299
14
+ chuk_puzzles_gym/games/binary/game.py,sha256=lRBweQIdzyRZm_jMPItZ1VAzAcsEEbxvGqjGwAlTTy0,16359
15
15
  chuk_puzzles_gym/games/bridges/__init__.py,sha256=ItdexP5RYfyTdxcvGY41fwQEi6tMCuMNdQpf8IDA70k,143
16
16
  chuk_puzzles_gym/games/bridges/config.py,sha256=JWhK6AmQzfFDs7uWiKqUayw4UQEcxPE6a5CdJ2HUm5A,910
17
- chuk_puzzles_gym/games/bridges/game.py,sha256=dgxJBa_ZzalfS7eX1f_LPAE2uk1R3mK7prrNgUc3j1k,18897
17
+ chuk_puzzles_gym/games/bridges/game.py,sha256=qEI8LFYKRASyTu0zomxvogWWdyPrEU989LlZ4TWascQ,18957
18
18
  chuk_puzzles_gym/games/cryptarithmetic/__init__.py,sha256=vnM5L1jvSEPlMrB_FoxDTLqi7HA6uFb2_leyLdjCVmY,261
19
19
  chuk_puzzles_gym/games/cryptarithmetic/commands.py,sha256=0feQk24ezYk_RubLv0I1URy_d2SPkswmecozYGx84p0,2514
20
20
  chuk_puzzles_gym/games/cryptarithmetic/config.py,sha256=IBCqJCVfb9UZTRDaDGGGr8qOpVyci9WsRiLmHyeFowU,960
21
- chuk_puzzles_gym/games/cryptarithmetic/game.py,sha256=daq46BEBdWgDDB4vmcWkbYbn-wfIjiJillveWZ5aI38,14939
21
+ chuk_puzzles_gym/games/cryptarithmetic/game.py,sha256=aQvA2u-X5lmuxqMhH3IJkeOPJGkUOX6ZwLzrlhb1uYo,15202
22
22
  chuk_puzzles_gym/games/einstein/__init__.py,sha256=ETFLNXXeR7fE8cQZb0cIdeupSdtReV5zr5cwS0Fs108,148
23
23
  chuk_puzzles_gym/games/einstein/config.py,sha256=JCnFSXWz3Vjd3k53-ZdS_x6UpvGKOYaoVMNtX-3ZcBc,829
24
24
  chuk_puzzles_gym/games/einstein/constants.py,sha256=8IXKJwaGupSqxAYlTFdc7og6xiqJC7xqYa5-CdwxeQQ,607
25
- chuk_puzzles_gym/games/einstein/game.py,sha256=nKxs8MsgtS9ASuub-gEJAaWRyFLou11qVngBIwbYxuY,13902
25
+ chuk_puzzles_gym/games/einstein/game.py,sha256=ifMC9IOigbPOX0Z_s1NetPaunOxPcfqeAWjOWyJF4lY,13962
26
26
  chuk_puzzles_gym/games/einstein/models.py,sha256=DtHJW65mlBgTynUJS0kPnynNJBbgAqPFWNC7jttQoxE,1076
27
27
  chuk_puzzles_gym/games/fillomino/__init__.py,sha256=A_aXhoY8ASfh3C_JGBASMJ6oWo8beN-YWc8gfPl1L9g,153
28
28
  chuk_puzzles_gym/games/fillomino/config.py,sha256=H7-CzBL3b4hYRe0QiOC1_hTXVojbbg-7vvZf5Q7idrQ,927
29
- chuk_puzzles_gym/games/fillomino/game.py,sha256=9s-CriEiYaLdX_hwLXUEpri3ZsK1aTsSRuJR_S2gaPQ,19319
29
+ chuk_puzzles_gym/games/fillomino/game.py,sha256=CPZnzWkm29beSxt4kmRPKImBQDqEDlDsVslYCf3Apys,19379
30
30
  chuk_puzzles_gym/games/futoshiki/__init__.py,sha256=kwgCnU2VGVEUX6oNBUb-k2PJ12EER3Do6az0JJAQVkQ,153
31
31
  chuk_puzzles_gym/games/futoshiki/config.py,sha256=zycoGtrGtXMhcbJkPf5-4WSgAYmCB6xFg5PNe7xjzTM,794
32
- chuk_puzzles_gym/games/futoshiki/game.py,sha256=nEBHySxb6c1e3ecJKEEFEomz2H80F69IYV360nB2rDQ,14076
32
+ chuk_puzzles_gym/games/futoshiki/game.py,sha256=yFSKXGsk8xfsLyzfmnz5UMbIUKqxJcuPJ7dyYWL-6lg,14136
33
33
  chuk_puzzles_gym/games/graph_coloring/__init__.py,sha256=iEh_AL8NngVPRGzOwgMAumpp1qU9SWNR7WWjsTGxsnE,248
34
- chuk_puzzles_gym/games/graph_coloring/commands.py,sha256=vEAws-gDQFV4H430jKSJ4AAZg-6K5vTq5fSfHgT8yt0,2601
34
+ chuk_puzzles_gym/games/graph_coloring/commands.py,sha256=2PeTAgZ_3eQ2WTmoYgsDy42JnkZpg6U-wdAvTTjfBDY,3279
35
35
  chuk_puzzles_gym/games/graph_coloring/config.py,sha256=i0fGexuy2fT0FN4CZdj6AEZwpXCBjB1OmAOMAqEAtFY,1085
36
- chuk_puzzles_gym/games/graph_coloring/game.py,sha256=08hbioYNhmjql4aLQqTNnxVljUJfHQtaHlYQ2EEr7ZY,12422
36
+ chuk_puzzles_gym/games/graph_coloring/game.py,sha256=OqE5VyjGqkEjcmJMP9SqrnjAEW0eBar6DFYKo43NP7k,12845
37
37
  chuk_puzzles_gym/games/hidato/__init__.py,sha256=nx9hhBuYUW6k5insmoY8T8ZYCQTNH-74yNmkvuN1Fv8,138
38
38
  chuk_puzzles_gym/games/hidato/config.py,sha256=tk8fwAVQvkVIfrBdschdGcdpvVr8r58zUI3P0UJ0-FA,913
39
- chuk_puzzles_gym/games/hidato/game.py,sha256=lMChcUxRLLkftdngL_1S2KFdYyqaOrJDAYA2cTA--L8,15037
39
+ chuk_puzzles_gym/games/hidato/game.py,sha256=4-oUnC5nOry5UJgsWqj-QTscgTndnm0ZQvokKJqNp_4,15097
40
40
  chuk_puzzles_gym/games/hitori/__init__.py,sha256=mSit-2ejHlBpFDNM7YAduZADBZgP7DTgLwvGUFT0VHs,138
41
41
  chuk_puzzles_gym/games/hitori/config.py,sha256=kcCBBhZr398hRwWU2FINarq2lJ1T2HZ4S-wlcm1X0UE,782
42
- chuk_puzzles_gym/games/hitori/game.py,sha256=MNAbD4moGDwqu1hPAMI3npygR-RH2W7iNcsmrYv-ejE,17265
42
+ chuk_puzzles_gym/games/hitori/game.py,sha256=z3OeEpiJufaZOIzCvKv6uYgSlpCusbhk5f8CEn9tlWM,17325
43
43
  chuk_puzzles_gym/games/kakuro/__init__.py,sha256=rYiQIzs1bSlRGk5DOcXgI_xud3gz_LO7CKekiC39ivM,138
44
44
  chuk_puzzles_gym/games/kakuro/config.py,sha256=QTbt6TOSgFpGfyDulZiTm13u9lzuOwJw_ZTKT5XO_Dw,910
45
- chuk_puzzles_gym/games/kakuro/game.py,sha256=9vAlTkEe6_jeR9m3yB7lEXZtXT3rlxQxaCuoNSzrTgc,14465
45
+ chuk_puzzles_gym/games/kakuro/game.py,sha256=tu4rRx4u4icVXt-FtyJCXY7XTl5SkEYghpslVboNT_A,14525
46
46
  chuk_puzzles_gym/games/kenken/__init__.py,sha256=4V9h7RS2nBr6ZoXD3xselQVcR8NxslzLFYguzJ4JPxw,138
47
47
  chuk_puzzles_gym/games/kenken/config.py,sha256=mcyku9kPnqdXJBR1pW_kLd_0ejeebg44PXGHQb7bsOw,896
48
48
  chuk_puzzles_gym/games/kenken/enums.py,sha256=8LUwUGTZR2MovoLMBrLcVwq3Mx8TVpojyJ9teSp2muk,247
49
- chuk_puzzles_gym/games/kenken/game.py,sha256=hBfWgyMnOD2-95vdsxeonuxipIjRb6uUtqBBrsTEMPc,18613
49
+ chuk_puzzles_gym/games/kenken/game.py,sha256=g11gYbCSBHV-RmXFlLh1FwxsEekG2PEuMdXNRWCRvng,18673
50
50
  chuk_puzzles_gym/games/kenken/models.py,sha256=PsZx9FCWr7B0sgEgiTYMmNSeRp6qriU7ES6i8rjBH5s,526
51
51
  chuk_puzzles_gym/games/killer_sudoku/__init__.py,sha256=t5wYyc4kyPPTlw3EzMVtu4MNF8HtJwt1OmaJqNqTI1w,168
52
52
  chuk_puzzles_gym/games/killer_sudoku/config.py,sha256=PU9vABsdODFnswrlEILykOLUMcvsy4T9JSKd0JxpJJc,833
53
- chuk_puzzles_gym/games/killer_sudoku/game.py,sha256=RrNlAk1EC_mHUt5AggLHCo2rhmaHuzt-YaNo-Mx2Dxs,18837
53
+ chuk_puzzles_gym/games/killer_sudoku/game.py,sha256=uOB7l0Mmt88Q7aBkVaZekNRNJFGi_SDVDYByYoAnl5E,18897
54
54
  chuk_puzzles_gym/games/killer_sudoku/models.py,sha256=HjTdW80adBpl5r_3uaNijrNR9YSwWlBdDrxLihtJJO4,434
55
55
  chuk_puzzles_gym/games/knapsack/__init__.py,sha256=JqpjOUl5w7S_9tNfzP28Qgo5sIqk6JDBFfSUnDi1sew,148
56
56
  chuk_puzzles_gym/games/knapsack/config.py,sha256=yxDnIgkOWuiDjaK__O4N2WHd5BNC8Jh-HFLI6jV7ckE,941
57
57
  chuk_puzzles_gym/games/knapsack/enums.py,sha256=_ByA1xdURmEIp6wlXDn-l2d7ffqYmMMBJdfsFM3vQ6I,171
58
- chuk_puzzles_gym/games/knapsack/game.py,sha256=Kd3aqJlv9iOd_2uDnVu6nPUD9lFGLWntyo89X0S2ZzU,12509
58
+ chuk_puzzles_gym/games/knapsack/game.py,sha256=tT1oY5bAjrk3003XApplGTLb7XIKpcOJHULP8wAVuRI,12569
59
59
  chuk_puzzles_gym/games/knapsack/models.py,sha256=OdtmDHsm-62Ej7HMVLqVfAh_ylsIIQl7Vv0DxmpRUqc,397
60
60
  chuk_puzzles_gym/games/lights_out/__init__.py,sha256=H3Uf6F_nyaJt1h0A6EWDRRWZERTZlqJ8URZAo8wFRXA,153
61
61
  chuk_puzzles_gym/games/lights_out/config.py,sha256=PujrybVGgmuKeY3bgpO7owUGbbHE6KbPqsqweMjiknk,944
62
- chuk_puzzles_gym/games/lights_out/game.py,sha256=b-u4XBYAeyyNINLh47btw3BFYIxi6DzUI8qQ_bu2HmE,8823
62
+ chuk_puzzles_gym/games/lights_out/game.py,sha256=KcvNdB54uDDk2Fq6fal-NExIKN3K9w-9yFFwZfxZXvE,8883
63
63
  chuk_puzzles_gym/games/logic_grid/__init__.py,sha256=ayL8uFmc7s8PCd1qjsQcMX9Y7yVvSBatQTwRjJVAriI,153
64
64
  chuk_puzzles_gym/games/logic_grid/config.py,sha256=OmffFq7Kmpnfi_ltITi-5lsbYz5LTbMWZL0k4VtEIUQ,977
65
65
  chuk_puzzles_gym/games/logic_grid/constants.py,sha256=20C07GHBY2swGDfJBFo3sXQt3Ag8GkH5McqyfniutFU,480
66
- chuk_puzzles_gym/games/logic_grid/game.py,sha256=8th3YWD2Kjbbs7RNKLdlda6bEQNZ8MRo4JBkz41CS48,11983
66
+ chuk_puzzles_gym/games/logic_grid/game.py,sha256=d1j-R1RS_-uX_oJpD3SC_E-U5joFvuretOEBDtcGx_I,12043
67
67
  chuk_puzzles_gym/games/logic_grid/models.py,sha256=lmE7jsP_-7oPzI081NGqm02DCZCQCwRy7T5_ecFHv1k,475
68
68
  chuk_puzzles_gym/games/mastermind/__init__.py,sha256=FIqOTKheJf5yexmMYJ0gU6prflJBqDrPasTAbdvYn9w,158
69
69
  chuk_puzzles_gym/games/mastermind/config.py,sha256=3QDlAbomhHhZAGnkkxknVXPSf1aVJ0b-mm3FycXcuBM,1117
70
- chuk_puzzles_gym/games/mastermind/game.py,sha256=-2ecVtVfXwNH3D-QtdkZP7cbK9IpD2h2mHaZ98mq0nI,10855
70
+ chuk_puzzles_gym/games/mastermind/game.py,sha256=FHs5yIWx6TvAoWpJ6sirwEkhx2rLM7wnRXfjRtLoBzc,10915
71
71
  chuk_puzzles_gym/games/minesweeper/__init__.py,sha256=mDSuSivp2m34GOn9XnchaKtIR_fhOExGhweGQUjhEkk,163
72
72
  chuk_puzzles_gym/games/minesweeper/config.py,sha256=W-K9RkxwlHZf1UUIS5275qaVzsTuRH2SDv3XG7G_HBw,902
73
73
  chuk_puzzles_gym/games/minesweeper/enums.py,sha256=VK6S4PGHXahHGl-ekRmdrhMr_D9L4FfuU-SJFoni-q8,196
74
- chuk_puzzles_gym/games/minesweeper/game.py,sha256=jCUgxT2i2BJE7WpL5hKgL_wme5Rv9vAdYOReWqh5iXY,16436
74
+ chuk_puzzles_gym/games/minesweeper/game.py,sha256=PbWTWpq8eQN8ubPniqB-ciENDo-krzUwQ2awVr7d1RA,16496
75
75
  chuk_puzzles_gym/games/nonogram/__init__.py,sha256=rjtRm0IumZfjTT02mmWWE0mNZ5LvJt7TpPA7drIvJUI,148
76
76
  chuk_puzzles_gym/games/nonogram/config.py,sha256=J0hPA3DqiPZjFg84YM-iqzHBWZDA1DuEqThJHTyoQVE,792
77
- chuk_puzzles_gym/games/nonogram/game.py,sha256=tRjmxnO2vUcS9KHfZ4DrOhYYhq1Wrl3MkQRibg9HL7o,10905
77
+ chuk_puzzles_gym/games/nonogram/game.py,sha256=TRIBT8ajRL2cuOXgi_3nrO29qsi90YkIb05LceQsinY,10965
78
78
  chuk_puzzles_gym/games/nqueens/__init__.py,sha256=BEWSaLz2x4Tj_0iXMPM-lgRPQLoaZOi-UKgRmzBO8KA,137
79
79
  chuk_puzzles_gym/games/nqueens/config.py,sha256=qb9XeyrswjaJ9Mmfxq1j3IdWOODJGrF0Uz6W5BTM75o,871
80
- chuk_puzzles_gym/games/nqueens/game.py,sha256=1JObwuQogOWkoVViym-3LheslEhsYOGiX7J57S6W5nU,11310
80
+ chuk_puzzles_gym/games/nqueens/game.py,sha256=mnwWKRWxiMRKqcpIDtvZRo_fun7RGXhLJuDycfCOaxI,11613
81
81
  chuk_puzzles_gym/games/numberlink/__init__.py,sha256=5mm2g8QD2PN8HUIUsJ5-tTBNdLlfQaagjYEjKLGo0UI,158
82
82
  chuk_puzzles_gym/games/numberlink/config.py,sha256=E6UsRkUkpR6bMWnQBkt7tYxEb77wBdeutEPSRa6lynw,871
83
- chuk_puzzles_gym/games/numberlink/game.py,sha256=8FW-28b08lhpjXAvAiWC8KU-Mc533sdzufxmoAXKdSw,12522
83
+ chuk_puzzles_gym/games/numberlink/game.py,sha256=x8x1PLjJvXsj5kfNhychi2OYCZ7Nquu0BC8vzWk-Pgc,12852
84
84
  chuk_puzzles_gym/games/nurikabe/__init__.py,sha256=maIZE3xItadevCnH_FMJiQr5VHMQrqiaOdnbBs87ndo,148
85
85
  chuk_puzzles_gym/games/nurikabe/config.py,sha256=UpET5L8UGFUuggDqrF6OaxVpGJt5iieVS8N5iBVT62U,914
86
86
  chuk_puzzles_gym/games/nurikabe/enums.py,sha256=NU5WyLK6t0df72zn5RL9T7_XGn8cDh4mHApuVE-V39c,218
87
- chuk_puzzles_gym/games/nurikabe/game.py,sha256=p12mh8pYwUwCiWH0HoDCwtvBaCV8cvKRuEtk2yqcaZc,22741
87
+ chuk_puzzles_gym/games/nurikabe/game.py,sha256=AVNNKJslJrUwdQ3niI4-16bIYoCABbNi4mLXkBlwa8Y,22801
88
88
  chuk_puzzles_gym/games/rush_hour/__init__.py,sha256=8JifY9z86sy-BktQlDQKBZQhLCw1ZwrZrpokX2cUTYs,252
89
89
  chuk_puzzles_gym/games/rush_hour/commands.py,sha256=_c91Lefd0R4HQskAWfaRe8HfrG5wfExTzPHbdxnspq4,1784
90
90
  chuk_puzzles_gym/games/rush_hour/config.py,sha256=HPNebhFSS82l0AYPe-HBtUy0FidpyizbfdTKV68RMGQ,1158
91
- chuk_puzzles_gym/games/rush_hour/game.py,sha256=-7T4xm7R8Nev-ZdLQqSYvTMEei6qihmp6fcLpLUMiPg,19842
91
+ chuk_puzzles_gym/games/rush_hour/game.py,sha256=me42wLbB_oM6HEFQXDijHvhdIYz3Ib2bWD08Tuyjaik,20046
92
92
  chuk_puzzles_gym/games/rush_hour/models.py,sha256=N9-So3PGFSy947LUy_Zf6UUYDsWN8KVYGS8qGJaCjDY,586
93
93
  chuk_puzzles_gym/games/scheduler/__init__.py,sha256=2dsZ3a1QcaGobAOHOtEwGMWGscnYrOtZpod8C_mg2XQ,153
94
94
  chuk_puzzles_gym/games/scheduler/config.py,sha256=mjznF2sKQRqcC8fO3KHbkdi48NMUV_GALmL0XzQ4rCY,1115
95
95
  chuk_puzzles_gym/games/scheduler/constants.py,sha256=KiySqF61dh2mcjiCfsXO5DX8IHmEU9w1xYsO7N5bJiw,242
96
96
  chuk_puzzles_gym/games/scheduler/enums.py,sha256=Npnop_EkNkfB4Q0s5LeF1oIMmIWFp8rBvMzsz31IfW4,174
97
- chuk_puzzles_gym/games/scheduler/game.py,sha256=yCbHn7T5whRRfyKY3PFWgFxxHbts41ihh0JOUMzbf0I,17331
97
+ chuk_puzzles_gym/games/scheduler/game.py,sha256=ykU3uepLUvNXMDarNcgzIwIIlGGk6UeZDacRyjn9jiA,17391
98
98
  chuk_puzzles_gym/games/scheduler/models.py,sha256=qh3IoOuuOoEVTcTP8CWpAdDlyL2HYYAfe-jj7WMqkxQ,520
99
99
  chuk_puzzles_gym/games/shikaku/__init__.py,sha256=-uBxf00nGMRvrF_m3Uk-Izzj24uOO9PaoVa6bInqmGo,143
100
100
  chuk_puzzles_gym/games/shikaku/config.py,sha256=orYfuE5y5iw8vPzgZaR9lofxMsMhLV6skr8nOhY9FSM,907
101
- chuk_puzzles_gym/games/shikaku/game.py,sha256=mujLR1mVaFhGPdpR3iasCz3qb6bkKUqmHME45_EiZOY,15541
101
+ chuk_puzzles_gym/games/shikaku/game.py,sha256=JrpGHRkPMNympVRZhGInVnHGwFkv6bVb6k2uE6rnLeg,15601
102
102
  chuk_puzzles_gym/games/skyscrapers/__init__.py,sha256=Y8XEu920EO97WgnRa9mtq_wtc8BFeG6jCBLowO5F2xY,156
103
103
  chuk_puzzles_gym/games/skyscrapers/config.py,sha256=K7LnI4Akz9QrfISIksOpbdMph0YAsbZO41AAG78ddoE,746
104
- chuk_puzzles_gym/games/skyscrapers/game.py,sha256=YbL53n6QvMv1ebi602JZjJxj2REG143Y3YV5p3LGNaA,10217
104
+ chuk_puzzles_gym/games/skyscrapers/game.py,sha256=kkW_q9N9CMnClvkzxxbtyBbSKEHmXq3sD-OqwfQYNNU,10510
105
105
  chuk_puzzles_gym/games/slitherlink/__init__.py,sha256=9KZSUINa7tJ6P1DF_Jb3qvGkYqZARPWYnZ3bZVGUVhE,163
106
106
  chuk_puzzles_gym/games/slitherlink/config.py,sha256=BunuL_XA5yOPI5AqMmOCF3iFcCgY8rLtokrye6wCNiA,804
107
- chuk_puzzles_gym/games/slitherlink/game.py,sha256=IDpvj8ynaQOCJJgFUeERY8NhKGiG0c4myfHTf0TGnv0,14580
107
+ chuk_puzzles_gym/games/slitherlink/game.py,sha256=woj_mC3niE70SfEaVpb8784jW_kFuSjqfKaD5Ibu2Cs,14640
108
108
  chuk_puzzles_gym/games/sokoban/__init__.py,sha256=F3gxLt0QeDqN3oku6Kg-FDAck5HjKlFXCgf_ZkuFsTc,143
109
109
  chuk_puzzles_gym/games/sokoban/config.py,sha256=An9LxYnPyx1r3orP3Cas-PFZYthbMnCvYJPNz4DEFXE,914
110
- chuk_puzzles_gym/games/sokoban/game.py,sha256=zWv3kSxEWq1W74VNkQEw_S73Y9Hr3_Ujj68Aan7mfUo,25567
110
+ chuk_puzzles_gym/games/sokoban/game.py,sha256=W58VxnJC_jy1jPmUCB5VLsT5Gj5xc70DK9_AjO5pnwo,25627
111
111
  chuk_puzzles_gym/games/star_battle/__init__.py,sha256=qsE4q6Awa9Ywj3oyDrruA5E-NOLEblKZEgrnrMB9Das,158
112
112
  chuk_puzzles_gym/games/star_battle/config.py,sha256=MUwFsoeIT9S7FwDGUSZ--3QePpoXSWRpDaLb7X0zcgs,958
113
- chuk_puzzles_gym/games/star_battle/game.py,sha256=D79GMWtvk08lrIioquTefRg2jii1IN933e7UvXRtRhM,14492
113
+ chuk_puzzles_gym/games/star_battle/game.py,sha256=f0c9gtvakhA7T0XPnoQS3TztI3DLOo0epoG83N2B-uM,14552
114
114
  chuk_puzzles_gym/games/sudoku/__init__.py,sha256=Q14HabjmkQc3QzqtA66WrAcHSoqLyv6A36_-KG8j-J4,205
115
115
  chuk_puzzles_gym/games/sudoku/commands.py,sha256=PbU0-7pkelP0sulyh8zudU47jVhpWhiUP0d0TOOmC0o,3004
116
116
  chuk_puzzles_gym/games/sudoku/config.py,sha256=pgr82NHcxvUAVZnAlsxeA7Iq3HrkhR1xAe_OGBOwAp8,779
117
- chuk_puzzles_gym/games/sudoku/game.py,sha256=CfBtSES1M8rrGGy8vB4HvmVbYerWA7n0FjQZuM7iPxI,11657
117
+ chuk_puzzles_gym/games/sudoku/game.py,sha256=35vB5x-KIs5z2b-CDV-dq5kifmVkoEkbLOx01xiCgKw,11717
118
118
  chuk_puzzles_gym/games/tents/__init__.py,sha256=iVxsZg7Juz3iHXTK8mfJZniFcMNnmAd2h2RjxR2TH40,133
119
119
  chuk_puzzles_gym/games/tents/config.py,sha256=gSi5epG5va8-a4ZQv5ekcFDkWQSYOSheX2j4FIs_I8Q,914
120
- chuk_puzzles_gym/games/tents/game.py,sha256=xU-WOjegPqTCq1X2oQ0k4VsGK4m4IS93KEdBaXIhv0k,15742
120
+ chuk_puzzles_gym/games/tents/game.py,sha256=JGPLYvIosCwjJYhi0FCtA3YUFsgQsD9L_BEArHSOPFM,15802
121
121
  chuk_puzzles_gym/models/__init__.py,sha256=dZzLWsyKE993o8HFfFkxTR7XjDwYK56rB-5clwW4zPg,930
122
122
  chuk_puzzles_gym/models/base.py,sha256=L7Zug9jUXJCOhD3wKJp0ppJZNTgroDQwdYMjvAaVVqc,1156
123
123
  chuk_puzzles_gym/models/config.py,sha256=12UkPlEEFzN1k9ZfJClpVqkp7E11MWriZVAH2RkfEM4,301
@@ -127,8 +127,8 @@ chuk_puzzles_gym/models/games.py,sha256=rnEW_Sl9xuZtvlBXBZfab34HrIhtUEiBdUSs_nvh
127
127
  chuk_puzzles_gym/trace/__init__.py,sha256=8JHaHxbTDhT9kv4e2e5Px4dCWuXY49OXmvzkMS4nKfw,273
128
128
  chuk_puzzles_gym/trace/generator.py,sha256=4pks0d_asoDE15QjM2VuzgFWTV1fZke_gHH2lVF8KVQ,34058
129
129
  chuk_puzzles_gym/utils/__init__.py,sha256=1AKPfRjT9YlBxxcA7qdKcvKBXdHJzfGtUWansrb_2VE,149
130
- chuk_puzzles_gym-0.10.dist-info/METADATA,sha256=zmWcNS_i2vj_IVVos5M81JBLXe6N25VT-EGCZhzeYrw,49933
131
- chuk_puzzles_gym-0.10.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
132
- chuk_puzzles_gym-0.10.dist-info/entry_points.txt,sha256=tJGHiH8wjkBev2SPNuXOLFkaXE76sW9ZFIMQw4pUj5E,181
133
- chuk_puzzles_gym-0.10.dist-info/top_level.txt,sha256=H3z9wKGl7CV1BPlO6t5lEtok6WW9rwGr5C1Dr3Kqx28,17
134
- chuk_puzzles_gym-0.10.dist-info/RECORD,,
130
+ chuk_puzzles_gym-0.10.1.dist-info/METADATA,sha256=HD-oYiDi5OTNMOjtvxQkB9aBuOBUARAy1RcXcjf4T2I,49935
131
+ chuk_puzzles_gym-0.10.1.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
132
+ chuk_puzzles_gym-0.10.1.dist-info/entry_points.txt,sha256=tJGHiH8wjkBev2SPNuXOLFkaXE76sW9ZFIMQw4pUj5E,181
133
+ chuk_puzzles_gym-0.10.1.dist-info/top_level.txt,sha256=H3z9wKGl7CV1BPlO6t5lEtok6WW9rwGr5C1Dr3Kqx28,17
134
+ chuk_puzzles_gym-0.10.1.dist-info/RECORD,,