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

Domains and Marks

Marks are what the player puts on the board — a Bulb here, the Sudoku digit 7 there, this edge Drawn. Each move either commits a mark at a coord or eliminates one from the set still possible there. The puzzle is solved when every coord’s set has narrowed to a single mark and every rule still holds.

The set of marks still possible at a coord at a given moment is its candidate set; the domain is the candidate set a fresh game starts with — every mark the genre permits at that position. This chapter covers both — the type of the values themselves (Mark) and how a candidate set narrows during play.

What’s at a coord

Every Floor coord starts a game with a full domain. For Sudoku, that’s 1..=9 per cell; for Akari, {Bulb, Empty} per floor cell; for Slitherlink, {Drawn, NotDrawn} per edge. Walls and holes have no domain — they aren’t decision targets.

Play narrows the candidate set. Each move eliminates a mark from a coord, or commits to a single one, and the propagator cascades the elimination through constraints: a Sudoku row’s Distinct rule removes the same digit from the other eight cells the moment one is committed; an Akari bulb removes Bulb from every cell its rays reach.

A coord ends up in one of three states:

  • Open — candidate set has more than one mark left. Play continues.
  • Solved — candidate set narrowed to a singleton. The remaining mark is the answer at this coord.
  • Contradicted — candidate set is empty. No legal mark; the engine reports a violation and the puzzle (or the current branch of the search) is unsolvable.

A puzzle is solved when every Floor coord is Solved and every Goal constraint is Satisfied (covered in the next chapter).

Mark

Every decision in the catalog is either binary (Akari bulb / empty, Slitherlink drawn / not-drawn, Nurikabe shaded / unshaded) or N-state numeric (Sudoku digits, Skyscrapers, Hashiwokakero bridge counts).

pub enum Mark {
    /// Binary mark — `false` / `true`. Per-puzzle prose labels
    /// ("Bulb" / "Empty", "Drawn" / "NotDrawn") are render concerns,
    /// not type concerns.
    Binary(bool),
    /// N-state numeric mark. The valid range is whatever the puzzle's
    /// `full_domain(coord)` emits — there's no library-fixed
    /// convention. Sudoku uses `1..=9` (matches the printed glyph);
    /// Hashiwokakero bridge slots use `0..=2` (zero bridges is a
    /// real value). The `u8` carries the value as displayed.
    Numeric(u8),
}

Keeping Binary distinct from Numeric(u8) is deliberate. Collapsing both into a single u8 would force binary genres to pick a convention (is bulb 0 or 1?) and propagator code to match-and-decode at every boundary. With the split, LOS sweeps and count constraints over Binary marks dispatch on the type itself, and Sudoku arithmetic stays unambiguous.

Numeric(u8) carries the displayed value — a Sudoku 5 is Numeric(5), not Numeric(4) zero-indexed. The u8 is wide enough for every digit any genre prints and narrow enough that a per-coord candidate set still fits in a small bitmask, which the fast solver relies on.