Performance roadmap
The originally-planned tiers all landed inside FastGame +
Propagator. FastSolver solves a 100×100 Akari in ~42 ms;
StandardGrader grades the same in ~210 ms. See the Fast solver
chapter for the full optimization log — what worked, what was
reverted, and what’s still open.
The ideas that landed:
- Region indices cached once at construction. Each constraint
resolves its
Regionto aVec<usize>of dense coord indices inPropagator::region_indices; the hot path walks the slice directly withcandidates[i]access. - First-class LOS / ray substrates.
Region::Los,Region::LosCross, andRegion::Neighbors4resolve natively viapuzzle.substrate().blocks_los(r, c)— a direct read on the cells grid (Material::Wallblocks,FloorandHolepass through), no trait-method dispatch. - Per-constraint aggregates. Each constraint caches
committed_with_mark/possible_with_mark;apply/undomaintain them;forced_moves_atreads them in O(1). - Per-cell counters (Tier 3, the big one). Puzzles declare
named counters via
Puzzle::cell_counters(); the propagator maintains them throughapply/undovia precomputed inverse- reach.Rule::CellMin { counter, min }propagates cell-by-cell through a dirty-cells queue instead of riding the AC-3 worklist. - Bitmask candidates. Per-coord
u32, one bit per mark in the puzzle’s universe. Apply / undo aremask & !bit/mask | bit. - MRV trial ordering + intra-sweep propagation in trial-1.
- LTO=fat + codegen-units=1 in release.
Still open: fused substrate-aware updates (a move that walks a ray AND emits forced moves on those same cells, in one pass), MAC / GAC / AC-2001, trial-2/N, smarter branch heuristics, parallelism in the solve loop, WASM measurement.