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

Genre expansion

Which genres ship next and why, revisited against what’s actually landed. The original sequencing (masyu → nurikabe → nurimisaki → heyawake → yajilin) was written when adding a genre meant ~7 files across core/, cli/, and eframe/, and when the OR-Tools encoder didn’t yet cover the rules that nurikabe-family genres need. Both of those have changed. This chapter rewrites the plan on top of the current infra.

What shipped vs. the plan

GenreOriginal planActual
akaridone at plan timedone
slitherlinkdone at plan timedone
sudokunot on planshipped (variant-CtC market fit; jumped the queue)
takuzunot on planshipped (worked example for the “smallest possible genre” path)
masyuPhase 1not started
nurikabePhase 2not started
nurimisakiPhase 3not started
heyawakePhase 4not started
yajilinPhase 5not started
hashiwokakerodeferrednow in scope (named in the business plan)

Sudoku displaced masyu because the business plan makes variant-Sudoku in the CtC orbit the beachhead audience. Takuzu displaced nurikabe because shipping a third tiny genre (single mod.rs + parse.rs + tests.rs, no pzv codec, no scaffold passes) validated the universal-genre architecture and gave the Adding a genre chapter a minimum-viable example to point at.

What changed in the infra

Three concrete shifts since the original plan was written.

Frontend wiring is one line

Adding a genre to CLI + eframe + bench is now one entry in the core::genre::GENRES slice, not three full files. The post-collapse architecture in Adding a genre spells this out: all three harnesses iterate the slice and dispatch through the runtime &'static Genre handle; per-genre cli/src/<genre>.rs and eframe/src/<genre>.rs no longer exist. The wiring cost per genre dropped from “a day or two” to “an hour.”

OR-Tools already encodes the stubbed rules

core/src/algorithm/ortools/solver.rs::encode_constraint is exhaustive on Rule and has arms for every variant currently stubbed in eval.rs:

Ruleeval.rs (FastSolver)OR-Tools encoder
ConnectedPendingimplemented
NoTwoByTwoPendingimplemented
ValueGroupedSizePendingimplemented
PolyominoPendingimplemented
BoundedSizePendingimplemented
Path { closed: false }Pendingimplemented
Sum / Increasing / MinDifferencePendingimplemented

This is the part that re-orders the roadmap. A genre whose rules are stubbed in eval.rs is not blocked from shipping — OR-Tools solves it correctly, the universal walker enumerates it, and trial-1 carries forces in the absence of a fast eval arm. The oracle works before the fast path does. Fast-path arms can land later when bench numbers force them.

Universal walker, generator, and tests

walk_canonical removed per-genre enumerators (~200 LOC each). scaffold_solve_derive covers most generation. Per-genre dataset-smoke tests are copy-paste. The dataset infra (puzzlekit-dataset repo, /private/tmp/penmark-data/) is in place. The cost of “adding a genre that uses already-shipped rules” is now mostly: write build_constraints, write the pzv parser, write tests.

The new ordering optimizes for two things: cheapest first (stay in flow, ship visible progress), and then heaviest infra-leverage (the genre that promotes the most stubs to real fast-path eval). This is a different ordering than the original plan, which optimized for engine-reuse compounding alone.

Phase 1 — masyu (cheapest delta)

Still the right first ship. Slitherlink-minus-clues with two new local rules (MasyuBlack, MasyuWhite). Reuses every existing slitherlink primitive: Path { closed: true }, DegreeIn, union-find, parse_url_frame, the pzv decode4Cell family. Eval-only first cut for the two new rules; trial-1 carries forces. OR-Tools needs new exhaustive arms for the two variants, but they’re local and small.

Why this ordering still holds: it’s the genre with the smallest possible delta against shipped code, so it’s the ideal “warm up the new architecture” ship and proves the once-per-genre cost is actually as low as the Adding a genre chapter claims.

Ship target: a few days. Definition of done unchanged from the original plan — 50-puzzle masyu dataset smoke passes, ortools cross-check, eframe pane renders.

Phase 2 — hashiwokakero

New addition from the business plan. Hashi is named explicitly as “within reach on the same architecture.” Engine-wise it sits between slither (edges) and nurikabe (Connected). It needs:

  • An edge-multiplicity rule — the existing edge substrate carries binary drawn/not drawn; Hashi needs {0, 1, 2} per edge slot. Either a new Mark::Numeric on edges (cleanest) or a new Rule::EdgeMultiplicity { allowed: [0,1,2] } over edges (smaller diff but encodes a marker as a constraint).
  • ExactCount per island over its incident edges (already shipped).
  • Connected { mark: Bridge } over the bridge edge set (stubbed in eval.rs; needs a real arm — see below).
  • A planarity / non-crossing cut: bridges can’t cross. Lazy cut pattern, same shape as the slither loop closure.

The Hashi solver work doubles as the forcing function for promoting Connected from stub to real. Once Connected has a fast eval arm, nurikabe / nurimisaki / heyawake / yajilin all benefit.

Why earlier than Phase 3+: the business plan explicitly calls Hashi out as part of the “Nikoli family within reach” pitch. CTC solves Hashi occasionally on YouTube. It’s a different shape of puzzle from the cell-coloring family, which broadens the engine demo without demanding the whole nurikabe-family stub-to-real push at once.

Ship target: ~2 weeks. Most of the time goes to the planarity cut and to making Connected fast enough for trial-1 to be useful.

Phase 3 — nurikabe (the high-leverage stub-to-real push)

This was Phase 2 in the original plan, and the underlying argument hasn’t changed: implementing real fast-path eval for Connected, NoTwoByTwo, and ValueGroupedSize unlocks four later genres simultaneously. What’s changed is that Hashi (Phase 2 above) already paid for Connected’s real arm, which makes nurikabe’s incremental cost smaller than originally estimated.

Three rules need real eval.rs arms after Hashi:

  • NoTwoByTwo { mark } — slow-path is trivial (walk every 2×2); fast-path is per-2×2 aggregate with force-when-three-and-one-undecided.
  • ValueGroupedSize — designed for nurikabe; per-value-seed BFS over committed cells of that mark, asserting size == value.
  • Connected (already done in Phase 2 if Hashi shipped first; otherwise here).

The pzv work is the standard decode4Cell family.

Ship target: ~3 weeks. Most of the time is the propagator work for the three rules.

Phase 4 — nurimisaki, heyawake (proves Phase 3 transports)

Both reuse Connected + NoTwoByTwo from Phase 3. Both ship fast on top of that base.

  • Nurimisaki adds one new local rule: Cape { at, len }. LOS ray of length len-1 white, terminated by black/wall, other three neighbors black/wall. Eval-only first cut.
  • Heyawake adds room geometry + adjacency. No new Rule variants — encodes via ExactCount per numbered room + AtMost(1) per adjacent black pair + per-straight-run Forbidden(ExactCount(black, k)) for the room-boundary rule. Lots of constraints, no new engine work.

Land them in the same phase because nurimisaki proves Phase 3’s stub promotions transport, and heyawake stress-tests the constraint count on existing rules.

Ship target: ~2 weeks combined.

Phase 5 — yajilin (the prize)

Original Phase 5; still the hardest. Cell-only Model A ({Black, WhiteLoop, Numbered} per cell, loop recovered from WhiteLoop set via Connected + DegreeIn(2)) reuses everything in place by then. Model B (edges + cells coupled biconditional) deferred until benches force it.

If Phase 3’s Connected arm proves not strong enough as a “single closed loop” check on cells, add a cell-substrate Path { closed: true } variant. The slither edge-based version demonstrates the algorithm is tractable.

Ship target: ~3 weeks.

Phases 1-5 in roughly two months

Estimate is honest if focused: masyu (3-5 days) + hashi (2 weeks)

  • nurikabe (3 weeks) + nurimisaki+heyawake (2 weeks) + yajilin (3 weeks). About 10 weeks of focused work, less if some genres ship faster than estimated, more if Connected’s fast eval arm turns into a multi-week project on its own.

This is materially faster than the original plan’s pace because of the architecture collapse: each genre is mostly one file in core/ plus three Vec entries elsewhere, and OR-Tools solves correctly today for every rule shape we care about. The gating work is per-rule fast-path eval, not per-genre wiring.

Deferred (still out of scope)

  • shakashaka — needs Mark::Triangle(Empty, NW, NE, SW, SE) (5-cardinal mark; engine assumes Binary/Numeric) and a RegionIsRectangle geometric rule with no analog. Revisit when there’s a concrete demand signal.
  • lits — tetromino placement is a different paradigm than per-coord candidate sets. Would need Rule::OneOfShapes and same-shape adjacency across rooms. Tractable but architecturally distinct.
  • dbchoco — region-pairing congruence; needs shape-comparison primitives.
  • Crossword, mazes, hidden-information genres — covered in the Puzzle families chapter; sister-track work, not in-scope for the CSP roadmap.

Locked decisions (carry over)

  • Trial-1 + OR-Tools oracle carries any genre as a “slow but correct” first cut. Fast-path eval lands when benches demand.
  • New genres are one-file-in-core/puzzles/<genre>/ + three Vec entries (CLI / eframe / bench). No per-genre frontend files.
  • pzv URL framing goes through core/src/puzzles/pzv.rs::parse_url_frame.
  • Each genre lands with a 50-puzzle dataset smoke test against the puzzlekit / puzzlink corpus.

Why this matters for the business plan

The business plan makes engine breadth a long-term moat: “the home for all Nikoli-style puzzles” is the embed-pitch differentiator no competitor can offer. The Phase 1 beachhead is variant-Sudoku — done — but Phase 4 of the business roadmap (“multi-genre expansion and market growth”) is the phase that turns the engine into a real pitch. Shipping six more genres in the next ~10 weeks moves Phase 4 from “long-term moat” to “ready to demo when the marketplace ships in the business plan’s Phase 2.”

The cheap-then-leveraged ordering (masyu → hashi → nurikabe → nurimisaki+heyawake → yajilin) means the engine demo gets visibly broader every couple of weeks, which is the showing-up-in-CtC-orbit cadence the business plan’s go-to-market section depends on.