Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

CLI

penmark <verb> <genre> [options]. The genre is a required positional, never a flag — every command knows up front which genre it’s running, and a missing genre is an immediate error rather than a subtle dispatch into the wrong defaults.

The verb set is still in flux; what’s wired today:

VerbPurpose
solveSolve every puzzle in a canonical dataset; report counts and timings.
gradeGrade one puzzle against a profile and print the trace.
grade-canonGrade every puzzle in a canonical dataset against a profile; print difficulty distribution.
generateFull pipeline — scaffold + solve + derive + reduce. Wired for akari, sudoku, slitherlink.
scaffoldPhase 1 only — wall/hole structural pass, no solver, no clues.
enumerateWalk uniquely-solvable puzzles of a given size for a genre.
importRead puzzlehound’s collated output and write canonical records to data/puzzles/<genre>.jsonl.gz.
profile-initCopy the embedded default profile to ~/.config/penmark/profiles/<genre>/ for editing.
profile-evalRun a profile against a canonical dataset; print the difficulty distribution.
list-implsPrint the hand-maintained list of genres + algorithms.

The verbs discover (capture specimens at every trial-search step) and coverage (per-technique hit-count report against persisted specimens) are described in the Enumerator chapter but haven’t shipped as CLI verbs yet. The mining loop runs ad-hoc against the enumerator until those land.

Config knobs are clap flags on the universal Cmd enum. There are no per-genre CLI files — GenericCli is a single non-generic impl that handles every command for every genre by dispatching through the runtime &'static Genre handle (genre.default_gen_density, genre.apply_cli_density, genre.parse_text, …). Adding a new option means one clap field on Cmd::Generate (or wherever) plus reading it from GenericCli::generate.

Parallelism is op-by-op: generate and the enumerator fan out via rayon::par_iter across puzzles in the work queue; one puzzle’s solve/grade is sequential. --threads=N is consistent where it applies and ignored on single-puzzle ops.