Refactor CLI/interface: init command, factory field, remove extras_size
- Rename `retro-gamer create` to `retro-gamer init` with positional args (GAME OUTPUT) instead of --game/--output flags - Add [tool.retro-gamer].factory = "module:attr" to declare the game factory function from pyproject.toml instead of relying on a hard-coded create_game attribute - Remove extras_size from user-facing config; it is now measured automatically from a sample observation and never declared - Update docs throughout: create→init, runs/→training/, add factory field documentation, clarify [model] vs [training] hyperparameter sections, remove stale version-history explanations - Bump version to 0.3.0; require retro-games>=2.5.0
This commit is contained in:
@@ -22,15 +22,12 @@ def cli():
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# retro-gamer create
|
||||
# retro-gamer init
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@cli.command()
|
||||
@click.option('--game', required=True,
|
||||
help='Game to train: a .py file path (e.g. my_game.py) or a Python module '
|
||||
'(e.g. retro.examples.snake)')
|
||||
@click.option('--output', required=True,
|
||||
help='Directory to create for this training run')
|
||||
@click.argument('game')
|
||||
@click.argument('output')
|
||||
@click.option('--learning-rate', default=DEFAULTS['learning_rate'], type=float,
|
||||
help=f"Adam optimizer learning rate (default {DEFAULTS['learning_rate']})")
|
||||
@click.option('--learning-rate-decay', default=DEFAULTS['learning_rate_decay'], type=float,
|
||||
@@ -60,11 +57,17 @@ def cli():
|
||||
@click.option('--prioritize-experiences/--no-prioritize-experiences',
|
||||
default=DEFAULTS['prioritize_experiences'],
|
||||
help='Use prioritized experience replay')
|
||||
def create(game, output, **hyperparams):
|
||||
def init(game, output, **hyperparams):
|
||||
"""Create a new training run directory.
|
||||
|
||||
Game metadata (actions, reward signal, etc.) is read from the
|
||||
[tool.retro-gamer] section of the game's pyproject.toml.
|
||||
GAME is a path to a game directory or a Python module name
|
||||
(e.g. games/snake or retro.examples.snake).
|
||||
|
||||
OUTPUT is the directory to create for this training run
|
||||
(e.g. training/snake).
|
||||
|
||||
Game metadata (actions, reward signal, factory function, etc.) is
|
||||
read from the [tool.retro-gamer] section of the game's pyproject.toml.
|
||||
Board size is read directly from the game. Hyperparameter options
|
||||
control how the trainer learns, not what it learns about.
|
||||
"""
|
||||
@@ -84,6 +87,9 @@ def create(game, output, **hyperparams):
|
||||
except (FileNotFoundError, ValueError) as e:
|
||||
raise click.ClickException(str(e))
|
||||
|
||||
if metadata.factory:
|
||||
game_config['factory'] = metadata.factory
|
||||
|
||||
game_factory = _load_factory(game_config)
|
||||
if metadata.board_size is None:
|
||||
g = game_factory()
|
||||
@@ -105,7 +111,7 @@ def create(game, output, **hyperparams):
|
||||
with open(run_dir / 'config.toml', 'wb') as f:
|
||||
tomli_w.dump(config, f)
|
||||
|
||||
click.echo(f"Created training run at {output}/config.toml")
|
||||
click.echo(f"Initialized training run at {output}/config.toml")
|
||||
click.echo(f" game : {game}")
|
||||
click.echo(f" board_size : {metadata.board_size[0]}×{metadata.board_size[1]}")
|
||||
click.echo(f" actions : {metadata.actions}")
|
||||
@@ -370,6 +376,21 @@ def _load_factory(game_config: dict):
|
||||
path = game_config.get('path')
|
||||
if path and path not in sys.path:
|
||||
sys.path.insert(0, path)
|
||||
|
||||
factory_str = game_config.get('factory')
|
||||
if factory_str:
|
||||
module_name, attr_name = factory_str.split(':', 1)
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
except ImportError as e:
|
||||
raise click.ClickException(f"Cannot import factory module '{module_name}': {e}")
|
||||
if not hasattr(module, attr_name):
|
||||
raise click.ClickException(
|
||||
f"Module '{module_name}' has no '{attr_name}' function "
|
||||
f"(from factory = {factory_str!r})"
|
||||
)
|
||||
return getattr(module, attr_name)
|
||||
|
||||
module_name = game_config['module']
|
||||
try:
|
||||
module = importlib.import_module(module_name)
|
||||
@@ -377,7 +398,8 @@ def _load_factory(game_config: dict):
|
||||
raise click.ClickException(f"Cannot import game module '{module_name}': {e}")
|
||||
if not hasattr(module, 'create_game'):
|
||||
raise click.ClickException(
|
||||
f"Module '{module_name}' has no create_game() function"
|
||||
f"Module '{module_name}' has no create_game() function. "
|
||||
"Add factory = \"module:create_game\" to [tool.retro-gamer] in pyproject.toml."
|
||||
)
|
||||
return module.create_game
|
||||
|
||||
|
||||
Reference in New Issue
Block a user