Directory layout
Penmark lives at threeemojis/penmark/ — its own top-level project,
not nested under ordo. The repo is a Cargo workspace:
threeemojis/penmark/
├── Cargo.toml — workspace manifest
├── core/ — engine: traits, library types, all algorithms
├── cli/ — `penmark` binary
├── eframe/ — local desktop app
├── data/ — canonical per-genre datasets + import inputs
│ ├── puzzles/<genre>.jsonl.gz — what the engine reads
│ └── dailyakari/ — one source's raw download script
└── scripts/ — bench-all, fetch-datasets, …
Inside core/src/:
core/src/
├── lib.rs — crate root, re-exports
├── coord.rs — Coord, OrthoDir, DiagDir, wire encoding
├── substrate.rs — Substrate, Material, MaterialVocab, Dimensions
├── mark.rs — Mark
├── region.rs — Region + resolution against a puzzle
├── constraint.rs — Constraint, Role, Rule, Violation, RuleKind
├── outcome.rs — Move, ConstraintOutcome, Status
├── puzzle.rs — Puzzle struct + GenreTrait (implementation interface)
├── genre.rs — runtime Genre struct + per-genre &'static Genre handles
├── overlay.rs — Overlay vocabulary (Highlight / Conflict / LoopFill / …)
├── render.rs — MarkRenderStyle (Lamp / Glyph / EdgeStroke)
├── tagged.rs — tagged-JSON wire format + Marks
├── game.rs — Game trait
├── cell_counter.rs — CellCounter declarations
├── symmetry.rs — orbit-based canonical-form helpers
├── db.rs — SQLite-backed persistence
├── algorithm/
│ ├── mod.rs — Solver, Grader traits, shared *Params
│ ├── eval.rs — generic constraint evaluator
│ ├── geometry.rs — region / reach geometry helpers
│ ├── generate.rs — GenConfig + scaffold_solve_derive
│ ├── enumerate.rs — EnumConfig + walk_canonical
│ ├── profile.rs — GradingProfile loading + validation
│ ├── fast/ — FastGame + Propagator + FastSolver
│ ├── simple/ — SimpleGame + SimpleSolver (brute-force ref)
│ ├── grader/ — StandardGrader + Technique trait + standard techniques
│ └── ortools/ — OR-Tools CP-SAT wrapper (cross-check oracle)
└── puzzles/ — one subdirectory per genre
├── pzv.rs — shared pzv-encoding helpers
├── store.rs — shared per-genre JSONL store
├── akari/ — mod + parse + pzv + scaffold + variants + tests
├── sudoku/ — mod + parse + pzv + generate + tests
└── slitherlink/ — mod + parse + pzv + generate + tests
Per-genre directories vary in shape — some genres ship a generate.rs
(when the universal recipe needs an override), some ship a
scaffold.rs (akari’s wall-style + clue-filter slices). The required
files are just mod.rs (with GenreTrait impl on the marker struct)
parse.rs. Enumeration runs through the universalalgorithm::enumerate::walk_canonicalfor every genre — there’s no per-genreenumerate.rsto write.
Adding a new genre is a new directory under core/src/puzzles/ plus
a one-line entry each in cli/src/main.rs::genres() and
eframe/src/main.rs’s genre list — see the Adding a genre chapter.
Inside the frontends:
cli/src/
├── main.rs — clap setup, dispatches to GenericCli
├── common.rs — shared CLI plumbing (load_dataset, summaries)
├── generic.rs — GenericCli — one impl handles every genre via &'static Genre
├── genre.rs — GenreCli trait (dyn dispatch surface)
├── grade_dataset.rs — grade against canonical datasets
├── import.rs — puzzlehound output → canonical pipeline
└── profile.rs — profile-init / profile-eval
eframe/src/
├── main.rs — egui app shell + genre routing
├── common.rs — shared widget code
├── genre.rs — GenreApp trait
├── games.rs — universal app behavior dispatching on puzzle.genre
├── painter.rs — universal puzzle painter (dispatches on genre.mark_render)
├── harness/ — solve/generate/grade harness panels
├── dataset_browser.rs — browse data/puzzles/<genre>.jsonl.gz
└── db_browser.rs — browse the SQLite db
Both frontends pulled per-genre files pre-collapse; they’re now
single implementations that dispatch on the &'static Genre handle
carried by each Puzzle. The fn-pointer table in Genre answers
every question they used to out-source per-genre.
Inside the bench harness:
core/benches/
├── penmark.rs — single bench file, dispatches all genres
├── common/mod.rs — solver_bench<G>, grader_bench<G>,
│ generator_bench<G>, pipeline_bench<G>,
│ load_fixtures<G>, load_dataset_corpus<G>
└── fixtures/<R>x<C>.json — checked-in akari fixture corpora
cargo bench --bench penmark -- akari/solver filters via criterion’s
name match.