Generators
Four static entry points, one shape. Namespace SimpleStates. Each clamps
the settings, rolls candidates until one matches (or MaxAttempts runs out),
analyzes it exactly, stamps the persisted summary, and attaches the transient
Analysis + Report.
| Member | Returns | Notes |
|---|---|---|
ClassicGenerator.GenerateVerified(settings) | StatesPuzzle | Classic combination boards. Honors solution style, dummy policy, rule shape, size window. |
OrderedGenerator.GenerateVerified(settings) | StatesPuzzle | Gated sequence boards; decoys and traps per policy. Trapped boards pass the no-deadlock proof. |
LightsOutGenerator.GenerateVerified(settings) | StatesPuzzle | Grid boards, scrambled from solved — always solvable; matched to the press window or exact target. Honors GoalMode/GoalPattern. |
Mod3Generator.GenerateVerified(settings) | StatesPuzzle | Trilight (mod-3) grid boards — same recipe over three states; scramble presses hit cells once or twice. Honors pattern goals. |
MaxAttempts, you still get the best candidate found —
puzzle.Report.Matched is false.Solvers
Static, allocation-light, pure C#. Every generated board arrives pre-analyzed; call these to re-analyze saved or hand-built boards (the analysis is transient and never serialized).
| Member | Returns | Notes |
|---|---|---|
ClassicSolver.Analyze(puzzle) | PuzzleAnalysis | Exhaustive subset enumeration up to ExhaustiveMaxSwitches (16); bigger boards auto-dispatch to Gf2Solver. |
Gf2Solver.Analyze(puzzle) | PuzzleAnalysis | Gaussian elimination over GF(2) — instant on 56 cells. Exact counts (2^nullity); full listing up to 2^MaxEnumeratedNullity (4,096) solutions, partial beyond (flagged in warnings). |
Mod3Solver.Analyze(puzzle) | PuzzleAnalysis | Gaussian elimination over ℤ₃ for Trilight. Exact counts (3^nullity); listing to 6,561. Solutions carry a cell twice when it needs two presses. |
OrderedSolver.Analyze(puzzle) | PuzzleAnalysis | BFS over board states: every shortest sequence (listing capped at 5,000 — unreachable at ≤ 8 switches), plus the step-by-step breakdown (Steps) with availability and blocked-press reasons. |
OrderedSolver.CountTraps(puzzle) | int | How many switches can re-lock a state (Toggle or Set-off effects). |
Both classic solvers produce identical analyses — they are cross-checked
field-for-field in the test harness. Malformed rule data (arrays shorter than
SwitchCount) is rejected with a warning instead of throwing.
StatesPuzzle
The serializable puzzle unit (stored inside the ScriptableObjects). Board state is a
bitmask: bit d set = state d on. Start = all
off (0), Goal = all on.
| Field | Type | Meaning |
|---|---|---|
Seed | string | The seed that produced it. |
Mode | PuzzleMode | Classic or Ordered (Lights Out boards are Classic + a grid). |
StateCount / SwitchCount | int | States to turn on / switches to press. |
ClassicRules | long[] | ClassicRules[p] = bitmask of states switch p toggles. Read row by row, this IS the incidence matrix. |
OrderedRules | OrderedRule[] | OrderedRules[p] = switch p's gate + effects (Ordered mode). |
GridWidth / GridHeight | int | > 0 = a grid board (Lights Out / Trilight). |
CellStates | int | 0/2 = binary grid; 3 = Trilight (IsMod3). |
CellStart / CellGoal | int[] | Trilight only: per-cell values 0..2 (index y*W+x). The bitmasks below are unused there. |
Start / Goal | long | Board-state bitmasks. On Lights Out, Goal carries the pattern (all-lit / 0 / painted). |
Solvable · SolutionCount · ShortestSize · Difficulty · Unique | summary | Persisted verdict, stamped at generation/verify time — readable without re-solving. |
Analysis / Report | transient | Full analysis + generation report; [NonSerialized], recomputed on demand. |
Settings | GenerationSettings | The recipe that made it (used by inspector Re-roll). Null for hand-built boards. |
Plus: AllStatesMask, IsLightsOut,
StampSummary(), factories StatesPuzzle.Classic(…) /
Ordered(…), and label helpers StateTag/SwitchTag/StateName/SwitchName
("ST3", "Switch 1"). Two-letter tags because switch and state share an initial — ST3 is a state, SW3 is a switch.
Ordered rules
An OrderedRule is a gate plus effects. The switch fires only when
every condition holds; then all effects apply.
| Type | Members | Meaning |
|---|---|---|
OrderedRule | Conditions, Effects | Lists of the below. |
Condition | State, RequiredOn | The state named by State must be off or on as stated. |
Effect | Type, State, SetOn | Toggle flips the state; Set forces it. Factories: Effect.Toggle(d), Effect.Set(d, on). |
The generator emits set-on chains, decoys, and (per policy) traps —
Effect.Set(state, false) gated on that state being on. The data model
carries the full condition/effect vocabulary, so hand-built rules can use all of it;
the BFS solver handles any combination.
PuzzleAnalysis
Everything the solver learns. Classic counts every valid switch set; Ordered counts shortest sequences.
| Member | Type | Meaning |
|---|---|---|
Solvable / IsUnique | bool | Any solution / exactly one. |
SolutionCount / ShortestSize | int | How many · switches in the smallest. |
ShortestSolutions / AllSolutions | List<int[]> | Switch indices — unordered sets (Classic) or sequences (Ordered). |
RequiredSwitches / OptionalSwitches / DummySwitches | int[] | Roles: in every / some / no solution. |
Diagnostics | RuleDiagnostics | States-per-switch spread, overlap count, single-state / all-state / dominant switches, isolated states, duplicate-rule groups. |
Steps | List<SequenceStep> | Ordered only: the shortest solve step by step — PressedSwitch, StateMask (the whole board after that press), ChangedStates, AvailableSwitches, and BlockedSwitches with reasons. |
ChallengeScore / Difficulty | int | Raw score · 1–5★ (0 = unrated). |
Warnings / DesignNotes | List<string> | Human-readable observations ("Exactly one solution (unique)."). |
GenerationSettings & GenerationReport
Every settings field is documented in the
Generating page's table.
Clamp() coerces all fields into valid ranges; Clone() copies a
recipe. The report:
| Field | Meaning |
|---|---|
AttemptsUsed | Candidates rolled. |
RuleRejects | Failed the rule-shape filters. |
QualityRejects | Failed the solution-quality filters (style, window, roles, traps). |
Matched | true = a board met every filter; false = best-effort fallback. |
Enums
| Enum | Values | Meaning |
|---|---|---|
PuzzleMode | Classic · Ordered | Combination vs sequence. Lights Out = Classic + grid. |
EffectType | Toggle · Set | Ordered effect kind. |
SolutionStyle | Single · Few · Many | How many valid solutions the generator accepts. |
DummyPolicy | None · Allow · Require | Decoy switches in no solution. |
TrapPolicy | None · Allow · Require | Ordered: re-locking decoys (deadlock-free verified). |
LightsOutGoal | AllLit · AllDark · Pattern | Grid-mode target; Pattern uses GenerationSettings.GoalPattern. |
ScriptableObjects
Storage lives in Runtime/ScriptableObjects. Both types implement
IPuzzleProvider (AppendTo(List<StatesPuzzle>)), so one
list can mix singles and packs.
| Type | Members | Notes |
|---|---|---|
StatesPuzzleAsset | Puzzle, Label, plus verdict pass-throughs (Solvable, IsUnique, SolutionCount, Difficulty, IsLightsOut, Seed…) and Set(data, label) | One puzzle. What Save ▸ Current writes. |
StatesPuzzlePack | PackName, Puzzles, Count, Get(i), Set / Add / Replace / Clear | An ordered set. What the demo's four pack slots take. |
PuzzleAssets | CreatePuzzle(data, label), CreatePack(puzzles, name) | Factories for building the assets from code (editor tooling uses these too). |
Reading a saved puzzle
using SimpleStates;
// Assigned in the inspector, or loaded any way you like:
[SerializeField] StatesPuzzlePack pack;
void Start()
{
StatesPuzzle p = pack.Get(0);
// The persisted verdict — no solving needed:
Debug.Log($"{p.Mode} · {p.StateCount} states · unique={p.Unique} · {p.Difficulty}★");
// Classic play is three lines of state:
long state = p.Start;
state ^= p.ClassicRules[2]; // press P3
bool won = state == p.Goal;
// Need the full picture (all solutions, roles, steps)? Recompute:
p.Analysis = p.IsMod3 ? Mod3Solver.Analyze(p)
: p.Mode == PuzzleMode.Ordered ? OrderedSolver.Analyze(p)
: ClassicSolver.Analyze(p);
}
For grid boards, cell (x, y) is index y * GridWidth + x
— the same index is both the state bit and the switch index (and the
CellStart/CellGoal slot on Trilight).
Demo runtime (SimpleStates.Demo)
The demo assembly's play loop is reusable as-is — StatesGame is a complete
referee for any board:
| Member | Notes |
|---|---|
LoadLevel(puzzle) | Builds the 3D board (via StatesLevelRenderer) and resets state. |
PressSwitch(i) / ResetLevel() | A press (classic toggles; ordered checks its gate first) / back to Start. |
StartAutoSolve(stepDelay) / CancelAutoSolve() | Play the best solution one press at a time; player input locks while AutoSolving. |
Puzzle · IsSolved · AutoSolving · StatesOnCount · MatchedCount · StateCount · MoveCount · Par | Live state for your UI — including presses vs the proven par and goal-matching counts on pattern boards. |
events: LevelLoaded, LevelReset, LevelSolved, Pressed, PressBlocked | Hook SFX/UI — the demo's beeper and HUD are wired off exactly these. |
Around it: StatesLevelRenderer (board + optional switch/state
prefab slots), StatesInput (click / R / F), StatesDemo (flow),
StatesMenu + StatesHud (Canvas UI), StatesBeeper
(procedural SFX). See Demos.