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

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 universal algorithm::enumerate::walk_canonical for every genre — there’s no per-genre enumerate.rs to 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.