pub struct NonogramGame {
pub width: usize,
pub height: usize,
pub cells: Vec<CellState>,
pub row_clues: Vec<Clue>,
pub col_clues: Vec<Clue>,
pub status: GameStatus,
/* private fields */
}Expand description
The nonogram game state: board dimensions, the hidden solution, the player’s grid, derived clues, and undo/redo history.
There is no losing state — every action is reversible via
Self::undo/Self::redo, and the game only ever transitions from
GameStatus::Playing to GameStatus::Won (or back, if an undo
reverts a winning move).
Fields§
§width: usizeBoard width in cells.
height: usizeBoard height in cells.
cells: Vec<CellState>Flat row-major grid of the player’s current marks (cells[y * width + x]).
row_clues: Vec<Clue>Clue for each row, top to bottom.
col_clues: Vec<Clue>Clue for each column, left to right.
status: GameStatusCurrent game status.
Implementations§
Source§impl NonogramGame
impl NonogramGame
Sourcepub fn from_grid(grid: Vec<Vec<bool>>) -> Self
pub fn from_grid(grid: Vec<Vec<bool>>) -> Self
Build a puzzle from an explicit solution grid (row-major, grid[y][x]).
Every row must have the same width. Use this for curated/picture puzzles.
§Panics
Panics if grid is empty, any row is empty, or rows have differing
lengths.
Sourcepub fn random(width: usize, height: usize, density: f32, seed: u64) -> Self
pub fn random(width: usize, height: usize, density: f32, seed: u64) -> Self
Build a procedurally-generated puzzle: each cell is independently
filled with probability density, reproducible via seed.
Regenerates until the result isn’t degenerate (not all-empty, not
all-filled), so the clues are always meaningful. density is
clamped to 0.05..=0.95 — the exact extremes would make a
non-degenerate result impossible (and the regeneration loop would
never terminate).
§Panics
Panics if width or height is zero.
Sourcepub fn reset(&mut self)
pub fn reset(&mut self)
Clear the player’s grid and undo/redo history, keeping the same
puzzle (solution and clues unchanged). To get a different puzzle,
construct a new NonogramGame.
pub fn idx(&self, x: usize, y: usize) -> usize
Sourcepub fn is_solution_filled(&self, x: usize, y: usize) -> bool
pub fn is_solution_filled(&self, x: usize, y: usize) -> bool
Whether (x, y) is part of the hidden solution.
Sourcepub fn toggle_fill(&mut self, x: usize, y: usize)
pub fn toggle_fill(&mut self, x: usize, y: usize)
Toggle a cell between Filled and Empty (clearing any Crossed
mark in the process). This is the left-click action.
Sourcepub fn toggle_cross(&mut self, x: usize, y: usize)
pub fn toggle_cross(&mut self, x: usize, y: usize)
Toggle a cell between Crossed and Empty (clearing any Filled
mark in the process). This is the right-click action.
Sourcepub fn clear_cell(&mut self, x: usize, y: usize)
pub fn clear_cell(&mut self, x: usize, y: usize)
Directly clear a cell back to Empty.
Sourcepub fn fill(&mut self, x: usize, y: usize)
pub fn fill(&mut self, x: usize, y: usize)
Directly set a cell to Filled, no toggling. Idempotent — a no-op
(no undo entry pushed) if the cell is already Filled. Intended for
click-and-drag painting, where the same target state is applied to
every cell the pointer passes over.
Sourcepub fn cross(&mut self, x: usize, y: usize)
pub fn cross(&mut self, x: usize, y: usize)
Directly set a cell to Crossed, no toggling. Idempotent, for the
same click-and-drag painting use case as Self::fill.
Sourcepub fn row_satisfied(&self, y: usize) -> bool
pub fn row_satisfied(&self, y: usize) -> bool
Whether row y’s currently-filled cells already match its clue. Used
both as a hint for the player (dim/strikethrough the clue) and,
together with Self::col_satisfied, as the win condition.
Sourcepub fn col_satisfied(&self, x: usize) -> bool
pub fn col_satisfied(&self, x: usize) -> bool
Whether column x’s currently-filled cells already match its clue.
See Self::row_satisfied.
pub fn can_undo(&self) -> bool
pub fn can_redo(&self) -> bool
pub fn undo(&mut self)
pub fn redo(&mut self)
Sourcepub fn random_logical(
width: usize,
height: usize,
density: f32,
seed: u64,
) -> Self
pub fn random_logical( width: usize, height: usize, density: f32, seed: u64, ) -> Self
Generate a puzzle guaranteed solvable via line-solving only (no guessing). Uses rejection sampling: generates random puzzles and keeps only those that pass the line-solvability check.
The odds of a random puzzle being line-solvable depend heavily on
width, height, and density, and not in an intuitive way —
bigger grids need higher density, not lower, because line-solving
relies on runs long enough to trigger overlap deduction. Some
combinations are effectively unreachable at any density: e.g. on a
square grid, a caller-supplied density much below the values found
empirically below will retry until it panics. Measured on square
grids (density is the lowest value found to reliably converge):
| grid size | density |
|---|---|
| 10x10 | ~0.45 |
| 11x11 | ~0.40 |
| 12x12 | ~0.45 |
| 13x13 | ~0.50 |
| 14x14 | ~0.55 |
| 15x15 | ~0.60 |
This table is empirical, not derived from a closed-form formula, and
does not extend cleanly to rectangular grids: a 20x8 grid and a 20x11
grid behave very differently even though they share a longer
dimension, so neither width nor height alone predicts difficulty.
If you’re picking density for a grid shape not covered above,
measure your own hit rate first — generate a batch of puzzles with
NonogramGame::random at your intended size/density and check how
many pass is_logically_solvable — rather than assuming this
function will find one quickly.
Per-attempt cost also grows with grid size, and is markedly higher right at the solvability threshold than comfortably above it (a near-miss puzzle takes many line-solving passes to stabilize before giving up, where a puzzle solvable outright — or clearly hopeless — converges fast). Pick a density with margin above the threshold, not the bare minimum.
§Panics
Panics if no logically solvable puzzle is found within 200 attempts.
That cap is intentionally small: unlike a puzzle generator that can
afford to grind for a good result, this runs synchronously wherever
it’s called (e.g. on a UI thread), so a hopeless (width, height, density) combination should fail fast rather than block for a long
time before eventually failing anyway.
Auto Trait Implementations§
impl Freeze for NonogramGame
impl RefUnwindSafe for NonogramGame
impl Send for NonogramGame
impl Sync for NonogramGame
impl Unpin for NonogramGame
impl UnsafeUnpin for NonogramGame
impl UnwindSafe for NonogramGame
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more