From 8ca9300ab305962c3fa65194e1e961b314673e67 Mon Sep 17 00:00:00 2001
From: Albrecht Degering
Date: Sun, 30 Aug 2026 23:21:56 +0200
Subject: [PATCH] feat: expand sudoku analysis and interoperability
---
CHANGELOG.md | 35 +
README.md | 61 +-
THIRD_PARTY_NOTICES.md | 10 +-
public/CHANGELOG.md | 35 +
public/README.md | 61 +-
public/THIRD_PARTY_NOTICES.md | 10 +-
src/components/AidMemoire.tsx | 378 ++++++++
src/components/CandidateLab.tsx | 408 ++++++++
src/components/ConstraintEditor.tsx | 396 +++++++-
src/components/GameplayHistoryDialog.tsx | 295 ++++++
src/components/GeneratorWorkspace.tsx | 68 +-
src/components/HelpDialog.tsx | 93 +-
src/components/HelpersWorkspace.tsx | 343 ++++++-
src/components/ImportExportDialog.tsx | 260 ++++--
src/components/SolveWorkspace.tsx | 16 +-
src/components/SudokuBoard.tsx | 518 +++++++++--
src/components/Workbench.tsx | 630 +++++++++++--
src/data/samples.ts | 51 +
src/domain/compile.ts | 40 +-
src/domain/geometry.ts | 33 +
src/domain/rules.ts | 379 +++++++-
src/domain/types.ts | 53 +-
src/domain/validation.ts | 252 ++++-
src/formats/boundedLz.ts | 247 +++++
src/formats/document.ts | 142 ++-
src/formats/fpuzzles.ts | 209 ++++-
src/formats/import.ts | 189 ++++
src/formats/index.ts | 5 +
src/formats/interoperability.ts | 37 +
src/formats/penpa.ts | 581 ++++++++++++
src/formats/share.ts | 30 +-
src/formats/sudokupad.ts | 489 ++++++++++
src/formats/types.ts | 73 +-
src/formats/visual.ts | 669 ++++++++++++++
src/helpers/candidates.ts | 379 ++++++++
src/helpers/index.ts | 2 +
src/helpers/sumLab.ts | 373 ++++++++
src/solver/exact.ts | 16 +
src/solver/logical.ts | 3 +-
src/solver/variantGenerator.ts | 86 +-
src/state/aidMemoire.ts | 404 ++++++++
src/state/gridNavigation.ts | 54 ++
src/state/playHistory.ts | 352 +++++++
src/storage/record.ts | 26 +
src/storage/types.ts | 3 +-
src/styles.css | 870 +++++++++++++++++-
tests/browser/workbench.spec.ts | 111 +++
tests/components/aidMemoire.test.tsx | 148 +++
tests/components/aidMemoireWorkbench.test.tsx | 139 +++
tests/components/candidateLab.test.tsx | 100 ++
.../gameplayHistoryWorkbench.test.tsx | 104 +++
tests/components/generatorWorkspace.test.tsx | 38 +
tests/components/helpersWorkspace.test.tsx | 117 +++
tests/components/importExportDialog.test.tsx | 186 ++++
tests/components/solveWorkspace.test.tsx | 44 +
tests/components/sudokuBoard.test.tsx | 176 +++-
tests/components/workbench.test.tsx | 144 +++
tests/data/samples.test.ts | 19 +-
tests/domain/extraBusyConstraints.test.ts | 505 ++++++++++
tests/formats/aidMemoireDocument.test.ts | 75 ++
tests/formats/document.test.ts | 31 +-
tests/formats/fpuzzles.test.ts | 95 ++
tests/formats/importSecurity.test.ts | 160 ++++
tests/formats/interoperability.test.ts | 175 ++++
tests/formats/visual.test.ts | 117 +++
tests/helpers/candidates.test.ts | 97 ++
tests/helpers/sumLab.test.ts | 146 +++
tests/solver/logical.test.ts | 22 +
tests/solver/variantGenerator.test.ts | 29 +
tests/state/aidMemoire.test.ts | 149 +++
tests/state/gridNavigation.test.ts | 27 +
tests/state/playHistory.test.ts | 176 ++++
tests/storage/aidMemoireProgress.test.ts | 72 ++
73 files changed, 12482 insertions(+), 384 deletions(-)
create mode 100644 src/components/AidMemoire.tsx
create mode 100644 src/components/CandidateLab.tsx
create mode 100644 src/components/GameplayHistoryDialog.tsx
create mode 100644 src/formats/boundedLz.ts
create mode 100644 src/formats/import.ts
create mode 100644 src/formats/interoperability.ts
create mode 100644 src/formats/penpa.ts
create mode 100644 src/formats/sudokupad.ts
create mode 100644 src/formats/visual.ts
create mode 100644 src/helpers/candidates.ts
create mode 100644 src/helpers/sumLab.ts
create mode 100644 src/state/aidMemoire.ts
create mode 100644 src/state/gridNavigation.ts
create mode 100644 src/state/playHistory.ts
create mode 100644 tests/components/aidMemoire.test.tsx
create mode 100644 tests/components/aidMemoireWorkbench.test.tsx
create mode 100644 tests/components/candidateLab.test.tsx
create mode 100644 tests/components/gameplayHistoryWorkbench.test.tsx
create mode 100644 tests/components/helpersWorkspace.test.tsx
create mode 100644 tests/components/importExportDialog.test.tsx
create mode 100644 tests/components/solveWorkspace.test.tsx
create mode 100644 tests/domain/extraBusyConstraints.test.ts
create mode 100644 tests/formats/aidMemoireDocument.test.ts
create mode 100644 tests/formats/importSecurity.test.ts
create mode 100644 tests/formats/interoperability.test.ts
create mode 100644 tests/formats/visual.test.ts
create mode 100644 tests/helpers/candidates.test.ts
create mode 100644 tests/helpers/sumLab.test.ts
create mode 100644 tests/state/aidMemoire.test.ts
create mode 100644 tests/state/gridNavigation.test.ts
create mode 100644 tests/state/playHistory.test.ts
create mode 100644 tests/storage/aidMemoireProgress.test.ts
diff --git a/CHANGELOG.md b/CHANGELOG.md
index efc7a58..a30e3ee 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,41 @@ All notable changes are documented here.
constraint family.
- Added bounded, seedable generation for classic and twelve variant families,
plus independent uniqueness and evidence-based difficulty assessment.
+- Added X-sum, skyscraper, quadruple and maximum-cell constraints across the
+ model, validator, exact solver, renderer, setter and f-puzzles interchange.
+- Added per-clue false semantics for liar/Wrogn puzzles, including bounded
+ impossible decoy numbers and a setter-wide false-clue mode.
+- Added the original, uniquely verified “Truth and lies” example demonstrating
+ true and false outside, inclusion, maximum, cage, line and pair clues.
+- Added outside-grid clue margins, combined dual X-sum/skyscraper labels,
+ outward maximum arrows and visually distinct false clues.
+- Added strict, bounded local imports for SudokuPad/CTC data and supported
+ Penpa+ long links, including source reporting and explicit incompatibility
+ lists instead of silently discarded constructs.
+- Added a compatibility check which never fetches SudokuPad short IDs or other
+ server-hosted puzzle references.
+- Added standalone SVG, high-resolution PNG and single-page PDF visual exports
+ for supported constraints, givens and optional solving progress.
+- Added named savepoints, isolated hypothesis branches, keep/discard decisions
+ and read-only replay of complete solving states.
+- Added an optional configurable aid-mémoire whose labelled scratch cells
+ support values, both note styles and colours without constraining the puzzle;
+ its state is preserved in undo, replay, Library progress and project/share
+ interchange.
+- Added a generalized Sum Lab with independent digit bounds, repeats,
+ required/excluded digits, selected-cell candidates, positional assignments
+ and reversible manual eliminations.
+- Added Candidate Lab filters, selected/touching/specific-house scopes, house
+ positions, strong conjugate/bivalue links, weak links and bounded board
+ overlays.
+- Added technique-targeted practice generation which mines a bounded,
+ deterministic set of uniquely checked puzzles and verifies the requested
+ technique in the logical solve path.
+- Added real ARIA row/gridcell semantics, detailed cell descriptions and
+ non-wrapping Arrow, Home/End, Control/Command + Home/End and Page Up/Down
+ keyboard navigation for the puzzle and scratch grids.
+- Made Killer cage repeat semantics explicit in the setter and independent of
+ whether a clue is required to be true or false.
## 0.1.0 - 2026-08-30
diff --git a/README.md b/README.md
index 941c390..7e8418c 100644
--- a/README.md
+++ b/README.md
@@ -8,33 +8,63 @@ release also runs independently from any static HTTPS host or local preview.
## Workspaces
- **Play** — keyboard, mouse and touch entry; multi-cell selection; values,
- corner/centre notes and colours; undo/redo; conflict highlighting; timer and
- local progress; optional digit-completion counts and matching-digit
- highlights.
+ corner/centre notes and colours; undo/redo; named savepoints, hypothesis
+ branches and read-only replay; conflict highlighting; timer and local
+ progress; optional digit-completion counts, matching-digit highlights and a
+ configurable non-constraining aid-mémoire.
- **Set** — givens, metadata, regions and typed constraints; uniqueness checks
with overlap-safe cage replacement and selection-based cage removal.
- **Generate** — seedable, bounded construction for classic and 12 variant
families; independent uniqueness verification and evidence-based difficulty
- assessment.
+ assessment; deterministic mining for a requested logical technique.
- **Solve** — exact solution counting plus an original human-style engine whose
steps include structured evidence, placements and eliminations.
-- **Helpers** — Killer combinations and candidate-aware assignments, 45-rule
- residuals, and Kropki, XV or inequality relation pairs.
+- **Helpers** — generalized sum combinations with positional candidates and
+ manual eliminations; selected-cell and house candidate-link analysis; Killer
+ combinations, 45-rule residuals, and Kropki, sum-pair or inequality pairs.
Classic grids and common variants share one bounded puzzle model: irregular
regions, diagonals, Killer cages, thermometers, arrows, Kropki and numbered
-5/10 sum-pair clues, inequalities, renban lines, palindromes, anti-knight,
-anti-king and non-consecutive rules. Thirteen bundled, uniquely checked examples
-demonstrate every supported constraint. Import accepts compact grid strings,
-this project's JSON format, share fragments, and the common f-puzzles fields
-supported by the model. Unknown constraints are reported rather than silently
-discarded.
+5/10 sum-pair clues, inequalities, renban lines, palindromes, X-sums,
+skyscrapers, quadruples, maximum cells, anti-knight, anti-king and
+non-consecutive rules. Every local clue can also be required to be false for
+liar/Wrogn constructions. Fourteen original bundled examples demonstrate every
+supported constraint and are exact-search verified as unique.
+
+The board exposes real row/gridcell semantics and concise per-cell state to
+assistive technology, including candidates, notes, colours, conflicts and
+touching variant clues. Arrow keys never wrap; Home/End, Control/Command +
+Home/End and Page Up/Down provide row- and grid-level navigation. The same
+navigation model is available in the aid-mémoire scratch grid.
+
+## Interoperability and visual export
+
+The import dialog recognises compact grids, Sudoku Tools JSON/share fragments,
+raw or inline f-puzzles data, SudokuPad/CTC (SCL) data, and Penpa+ self-contained
+long links. It never follows a URL or resolves a server-side short ID. Imports
+are size/decompression bounded, report their detected source before applying,
+and stop with a list when a construct cannot be represented faithfully.
+
+SudokuPad compatibility currently preserves cells, givens and progress, notes,
+regions, metadata, solutions, Killer cages and supported global rules. Penpa+
+compatibility is intentionally narrower: square, unrotated Sudoku grids with
+givens/progress, thermometers and arrows. Visual-only lines, overlays, custom
+symbols and other unsupported geometry are reported rather than ignored.
+f-puzzles export likewise refuses false clues it cannot preserve.
+
+Export can produce a standalone SVG, a high-resolution PNG, or a single-page
+PDF entirely in the browser. Each visual uses the same trusted puzzle model and
+renders region boundaries, supported variant clues and givens; current values,
+notes and cell colours can be included or omitted with the progress switch.
Generation supports only size/variant combinations which pass the bounded
construction checks. The requested difficulty controls clue-removal targets;
the displayed 0–100 result is calculated afterwards from logical techniques,
clue load and reproducible exact-search evidence. A timeout or node limit is
reported as unknown and never promoted to a uniqueness or difficulty claim.
+Technique practice performs a bounded, deterministic search and returns a
+puzzle only when its independently analysed solve path actually contains the
+requested technique.
## Privacy and storage
@@ -46,7 +76,8 @@ Exported files and share URLs contain the puzzle data you chose to include.
SudokuPad short IDs identify server-hosted records and therefore cannot be
resolved by this deliberately offline application. Embedded f-puzzles payloads
-are accepted without contacting their originating site.
+and user-provided Sudoku Tools JSON documents are accepted without contacting
+their originating site. Externally authored puzzle definitions are not bundled.
## Development
@@ -76,8 +107,8 @@ browser workflows and Toolbox contract before creating a deterministic
The interaction ideas and published solving concepts explored by SudokuPad,
SudokuWiki and the wider puzzle community informed the product requirements.
-No code, artwork, wording or proprietary puzzle data from those applications is
-included. Algorithms and explanations in this repository are independently
+No externally authored puzzle definitions, code, artwork or documentation text
+is bundled. Algorithms, UI, original examples and explanations are independently
implemented from mathematical rules and public standards.
## Licence
diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md
index ad56c53..f7ca811 100644
--- a/THIRD_PARTY_NOTICES.md
+++ b/THIRD_PARTY_NOTICES.md
@@ -16,4 +16,12 @@ runtime package. Development-only packages build and test the source but are
not shipped as runtime modules.
SudokuPad, SudokuWiki and HoDoKu are research references only. Their source,
-documentation, wording, visual assets and puzzle data are not bundled or copied.
+documentation wording, visual assets and externally authored puzzle data are
+not bundled or copied.
+
+The local compatibility code was tested against the open format behaviour in
+[Penpa+](https://github.com/swaroopg92/penpa-edit) (MIT),
+[penpa-to-scl](https://github.com/marktekfan/penpa-to-scl) (MIT), and
+[SudokuPad sudokutools](https://github.com/SudokuPad/sudokutools) (MPL-2.0).
+Those projects are format references only: no upstream executable code,
+artwork, documentation text or puzzle data is distributed with Sudoku Tools.
diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md
index efc7a58..a30e3ee 100644
--- a/public/CHANGELOG.md
+++ b/public/CHANGELOG.md
@@ -16,6 +16,41 @@ All notable changes are documented here.
constraint family.
- Added bounded, seedable generation for classic and twelve variant families,
plus independent uniqueness and evidence-based difficulty assessment.
+- Added X-sum, skyscraper, quadruple and maximum-cell constraints across the
+ model, validator, exact solver, renderer, setter and f-puzzles interchange.
+- Added per-clue false semantics for liar/Wrogn puzzles, including bounded
+ impossible decoy numbers and a setter-wide false-clue mode.
+- Added the original, uniquely verified “Truth and lies” example demonstrating
+ true and false outside, inclusion, maximum, cage, line and pair clues.
+- Added outside-grid clue margins, combined dual X-sum/skyscraper labels,
+ outward maximum arrows and visually distinct false clues.
+- Added strict, bounded local imports for SudokuPad/CTC data and supported
+ Penpa+ long links, including source reporting and explicit incompatibility
+ lists instead of silently discarded constructs.
+- Added a compatibility check which never fetches SudokuPad short IDs or other
+ server-hosted puzzle references.
+- Added standalone SVG, high-resolution PNG and single-page PDF visual exports
+ for supported constraints, givens and optional solving progress.
+- Added named savepoints, isolated hypothesis branches, keep/discard decisions
+ and read-only replay of complete solving states.
+- Added an optional configurable aid-mémoire whose labelled scratch cells
+ support values, both note styles and colours without constraining the puzzle;
+ its state is preserved in undo, replay, Library progress and project/share
+ interchange.
+- Added a generalized Sum Lab with independent digit bounds, repeats,
+ required/excluded digits, selected-cell candidates, positional assignments
+ and reversible manual eliminations.
+- Added Candidate Lab filters, selected/touching/specific-house scopes, house
+ positions, strong conjugate/bivalue links, weak links and bounded board
+ overlays.
+- Added technique-targeted practice generation which mines a bounded,
+ deterministic set of uniquely checked puzzles and verifies the requested
+ technique in the logical solve path.
+- Added real ARIA row/gridcell semantics, detailed cell descriptions and
+ non-wrapping Arrow, Home/End, Control/Command + Home/End and Page Up/Down
+ keyboard navigation for the puzzle and scratch grids.
+- Made Killer cage repeat semantics explicit in the setter and independent of
+ whether a clue is required to be true or false.
## 0.1.0 - 2026-08-30
diff --git a/public/README.md b/public/README.md
index 941c390..7e8418c 100644
--- a/public/README.md
+++ b/public/README.md
@@ -8,33 +8,63 @@ release also runs independently from any static HTTPS host or local preview.
## Workspaces
- **Play** — keyboard, mouse and touch entry; multi-cell selection; values,
- corner/centre notes and colours; undo/redo; conflict highlighting; timer and
- local progress; optional digit-completion counts and matching-digit
- highlights.
+ corner/centre notes and colours; undo/redo; named savepoints, hypothesis
+ branches and read-only replay; conflict highlighting; timer and local
+ progress; optional digit-completion counts, matching-digit highlights and a
+ configurable non-constraining aid-mémoire.
- **Set** — givens, metadata, regions and typed constraints; uniqueness checks
with overlap-safe cage replacement and selection-based cage removal.
- **Generate** — seedable, bounded construction for classic and 12 variant
families; independent uniqueness verification and evidence-based difficulty
- assessment.
+ assessment; deterministic mining for a requested logical technique.
- **Solve** — exact solution counting plus an original human-style engine whose
steps include structured evidence, placements and eliminations.
-- **Helpers** — Killer combinations and candidate-aware assignments, 45-rule
- residuals, and Kropki, XV or inequality relation pairs.
+- **Helpers** — generalized sum combinations with positional candidates and
+ manual eliminations; selected-cell and house candidate-link analysis; Killer
+ combinations, 45-rule residuals, and Kropki, sum-pair or inequality pairs.
Classic grids and common variants share one bounded puzzle model: irregular
regions, diagonals, Killer cages, thermometers, arrows, Kropki and numbered
-5/10 sum-pair clues, inequalities, renban lines, palindromes, anti-knight,
-anti-king and non-consecutive rules. Thirteen bundled, uniquely checked examples
-demonstrate every supported constraint. Import accepts compact grid strings,
-this project's JSON format, share fragments, and the common f-puzzles fields
-supported by the model. Unknown constraints are reported rather than silently
-discarded.
+5/10 sum-pair clues, inequalities, renban lines, palindromes, X-sums,
+skyscrapers, quadruples, maximum cells, anti-knight, anti-king and
+non-consecutive rules. Every local clue can also be required to be false for
+liar/Wrogn constructions. Fourteen original bundled examples demonstrate every
+supported constraint and are exact-search verified as unique.
+
+The board exposes real row/gridcell semantics and concise per-cell state to
+assistive technology, including candidates, notes, colours, conflicts and
+touching variant clues. Arrow keys never wrap; Home/End, Control/Command +
+Home/End and Page Up/Down provide row- and grid-level navigation. The same
+navigation model is available in the aid-mémoire scratch grid.
+
+## Interoperability and visual export
+
+The import dialog recognises compact grids, Sudoku Tools JSON/share fragments,
+raw or inline f-puzzles data, SudokuPad/CTC (SCL) data, and Penpa+ self-contained
+long links. It never follows a URL or resolves a server-side short ID. Imports
+are size/decompression bounded, report their detected source before applying,
+and stop with a list when a construct cannot be represented faithfully.
+
+SudokuPad compatibility currently preserves cells, givens and progress, notes,
+regions, metadata, solutions, Killer cages and supported global rules. Penpa+
+compatibility is intentionally narrower: square, unrotated Sudoku grids with
+givens/progress, thermometers and arrows. Visual-only lines, overlays, custom
+symbols and other unsupported geometry are reported rather than ignored.
+f-puzzles export likewise refuses false clues it cannot preserve.
+
+Export can produce a standalone SVG, a high-resolution PNG, or a single-page
+PDF entirely in the browser. Each visual uses the same trusted puzzle model and
+renders region boundaries, supported variant clues and givens; current values,
+notes and cell colours can be included or omitted with the progress switch.
Generation supports only size/variant combinations which pass the bounded
construction checks. The requested difficulty controls clue-removal targets;
the displayed 0–100 result is calculated afterwards from logical techniques,
clue load and reproducible exact-search evidence. A timeout or node limit is
reported as unknown and never promoted to a uniqueness or difficulty claim.
+Technique practice performs a bounded, deterministic search and returns a
+puzzle only when its independently analysed solve path actually contains the
+requested technique.
## Privacy and storage
@@ -46,7 +76,8 @@ Exported files and share URLs contain the puzzle data you chose to include.
SudokuPad short IDs identify server-hosted records and therefore cannot be
resolved by this deliberately offline application. Embedded f-puzzles payloads
-are accepted without contacting their originating site.
+and user-provided Sudoku Tools JSON documents are accepted without contacting
+their originating site. Externally authored puzzle definitions are not bundled.
## Development
@@ -76,8 +107,8 @@ browser workflows and Toolbox contract before creating a deterministic
The interaction ideas and published solving concepts explored by SudokuPad,
SudokuWiki and the wider puzzle community informed the product requirements.
-No code, artwork, wording or proprietary puzzle data from those applications is
-included. Algorithms and explanations in this repository are independently
+No externally authored puzzle definitions, code, artwork or documentation text
+is bundled. Algorithms, UI, original examples and explanations are independently
implemented from mathematical rules and public standards.
## Licence
diff --git a/public/THIRD_PARTY_NOTICES.md b/public/THIRD_PARTY_NOTICES.md
index ad56c53..f7ca811 100644
--- a/public/THIRD_PARTY_NOTICES.md
+++ b/public/THIRD_PARTY_NOTICES.md
@@ -16,4 +16,12 @@ runtime package. Development-only packages build and test the source but are
not shipped as runtime modules.
SudokuPad, SudokuWiki and HoDoKu are research references only. Their source,
-documentation, wording, visual assets and puzzle data are not bundled or copied.
+documentation wording, visual assets and externally authored puzzle data are
+not bundled or copied.
+
+The local compatibility code was tested against the open format behaviour in
+[Penpa+](https://github.com/swaroopg92/penpa-edit) (MIT),
+[penpa-to-scl](https://github.com/marktekfan/penpa-to-scl) (MIT), and
+[SudokuPad sudokutools](https://github.com/SudokuPad/sudokutools) (MPL-2.0).
+Those projects are format references only: no upstream executable code,
+artwork, documentation text or puzzle data is distributed with Sudoku Tools.
diff --git a/src/components/AidMemoire.tsx b/src/components/AidMemoire.tsx
new file mode 100644
index 0000000..c477e11
--- /dev/null
+++ b/src/components/AidMemoire.tsx
@@ -0,0 +1,378 @@
+import { useRef, type CSSProperties, type KeyboardEvent } from "react";
+import {
+ aidMemoireCellDescription,
+ clearAidMemoireEntries,
+ compactAidMemoireColumns,
+ configureAidMemoire,
+ enterAidMemoireCell,
+ eraseAidMemoireCell,
+ labelAidMemoireCell,
+ MAX_AID_MEMOIRE_CELLS,
+ MAX_AID_MEMOIRE_COLUMNS,
+ resetAidMemoire,
+ setAidMemoireEnabled,
+ type AidMemoireState,
+} from "../state/aidMemoire";
+import {
+ maskValues,
+ symbolFor,
+ valueForKey,
+ type EntryMode,
+} from "../state/session";
+
+interface AidMemoireProps {
+ readonly size: number;
+ readonly state: AidMemoireState;
+ readonly selectedCell: number;
+ readonly active: boolean;
+ readonly readOnly?: boolean;
+ readonly mode: EntryMode;
+ readonly onStateChange: (state: AidMemoireState) => void;
+ readonly onSelect: (cell: number) => void;
+ readonly onMode: (mode: EntryMode) => void;
+}
+
+const modes: ReadonlyArray<{ mode: EntryMode; label: string; key: string }> = [
+ { mode: "value", label: "Value", key: "Z" },
+ { mode: "corner", label: "Corner", key: "X" },
+ { mode: "center", label: "Centre", key: "C" },
+ { mode: "color", label: "Colour", key: "V" },
+];
+
+export function AidMemoire({
+ size,
+ state,
+ selectedCell,
+ active,
+ readOnly = false,
+ mode,
+ onStateChange,
+ onSelect,
+ onMode,
+}: AidMemoireProps) {
+ const cellRefs = useRef>([]);
+ const selected = Math.max(0, Math.min(state.cells.length - 1, selectedCell));
+
+ const selectAndFocus = (cell: number) => {
+ const next = Math.max(0, Math.min(state.cells.length - 1, cell));
+ onSelect(next);
+ cellRefs.current[next]?.focus();
+ };
+
+ const handleKeyDown = (
+ cell: number,
+ event: KeyboardEvent,
+ ) => {
+ const row = Math.floor(cell / state.columns);
+ const column = cell % state.columns;
+ const rowStart = row * state.columns;
+ const rowEnd = Math.min(
+ state.cells.length - 1,
+ rowStart + state.columns - 1,
+ );
+ let destination: number | undefined;
+ if (event.key === "ArrowLeft") destination = Math.max(rowStart, cell - 1);
+ if (event.key === "ArrowRight") destination = Math.min(rowEnd, cell + 1);
+ if (event.key === "ArrowUp") {
+ destination = cell - state.columns >= 0 ? cell - state.columns : cell;
+ }
+ if (event.key === "ArrowDown") {
+ destination =
+ cell + state.columns < state.cells.length ? cell + state.columns : cell;
+ }
+ if (event.key === "Home") {
+ destination = event.ctrlKey || event.metaKey ? 0 : rowStart;
+ }
+ if (event.key === "End") {
+ destination =
+ event.ctrlKey || event.metaKey ? state.cells.length - 1 : rowEnd;
+ }
+ if (event.key === "PageUp") destination = column;
+ if (event.key === "PageDown") {
+ const lastRowStart =
+ Math.floor((state.cells.length - 1) / state.columns) * state.columns;
+ destination = Math.min(state.cells.length - 1, lastRowStart + column);
+ }
+ if (destination !== undefined) {
+ event.preventDefault();
+ selectAndFocus(destination);
+ return;
+ }
+ if (readOnly) return;
+ if (
+ event.key === "Backspace" ||
+ event.key === "Delete" ||
+ event.key === "0"
+ ) {
+ event.preventDefault();
+ onStateChange(eraseAidMemoireCell(state, cell, mode));
+ return;
+ }
+ if (!event.ctrlKey && !event.metaKey && !event.altKey) {
+ const modeKey: Record = {
+ z: "value",
+ x: "corner",
+ c: "center",
+ v: "color",
+ };
+ const nextMode = modeKey[event.key.toLowerCase()];
+ if (nextMode !== undefined) {
+ event.preventDefault();
+ onMode(nextMode);
+ return;
+ }
+ const value = valueForKey(event.key, mode === "color" ? 8 : size);
+ if (value !== null) {
+ event.preventDefault();
+ onStateChange(enterAidMemoireCell(state, cell, mode, value, size));
+ }
+ }
+ };
+
+ const configure = (cellCount: number, columns: number) => {
+ const next = configureAidMemoire(state, size, cellCount, columns);
+ onStateChange(next);
+ if (selected >= next.cells.length) onSelect(next.cells.length - 1);
+ };
+
+ return (
+
+
+
+
Optional scratch cells
+
Aid-mémoire
+
+ Track contextual sets, pseudo-digits or mappings. These cells do not
+ constrain the Sudoku.
+
+ Inspect legal candidates without changing any notes. A solid line is
+ strong (at least one endpoint is true); a dashed line is weak (both
+ endpoints cannot be true).
+
+ The board overlay is capped at {OVERLAY_LINK_LIMIT} links for
+ responsiveness. Focus a listed link or narrow the filters to see a
+ precise relationship.
+
@@ -187,7 +236,9 @@ export function GeneratorWorkspace({
Difficulty is an estimate from reproducible solver evidence, not a
- universal promise. Uniqueness is never claimed after a safety limit.
+ universal promise. Technique practice only returns a puzzle whose
+ logical path contains the requested move. Uniqueness is never claimed
+ after a safety limit.
)}
diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx
index ecb63a3..49c65ad 100644
--- a/src/components/HelpDialog.tsx
+++ b/src/components/HelpDialog.tsx
@@ -14,11 +14,12 @@ export function HelpDialog({
Five complementary workspaces
Play keeps values, two kinds of notes, colours,
- history and elapsed time. Set edits clues and
- constraints. Generate constructs and rates bounded,
- seedable variants. Solve explains logical steps and
- can verify uniqueness. Helpers answers focused
- questions without changing the board.
+ branches, replay and elapsed time. Set edits clues
+ and constraints. Generate constructs and rates
+ bounded, seedable variants. Solve explains logical
+ steps and can verify uniqueness. Helpers answers
+ focused sum, candidate and relation questions without changing the
+ board.
@@ -32,6 +33,18 @@ export function HelpDialog({
Shift + arrows
Extend the selection
+
+
Home / End
+
Move to the first or last cell in the row
+
+
+
Ctrl/⌘ + Home/End
+
Move to the first or last cell in the grid
+
+
+
Page Up / Down
+
Move to the first or last row in the same column
+
1–9 / A–G
Enter the selected symbol
@@ -64,13 +77,62 @@ export function HelpDialog({
explanation.
+
+
Branches, savepoints and replay
+
+ Open History & branches while playing to name a
+ checkpoint, isolate a hypothesis, or inspect an earlier grid.
+ Discarding a hypothesis restores its exact starting state but keeps
+ the abandoned path available in replay. Replayed grids are read-only
+ until you return live or deliberately branch from that step. This
+ working history stays in the current browser session; save the
+ puzzle to the Library for durable puzzle progress.
+
+
+
+
Sum and Candidate Labs
+
+ Sum Lab enumerates bounded combinations and, when selected board
+ cells are enabled, candidate-compatible assignments per position.
+ Required, excluded and manually eliminated combinations remain local
+ helper state. Candidate Lab inspects selected cells and houses,
+ filters one or more digits, and can overlay strong and weak links
+ without changing handwritten notes.
+
+
+
+
Aid-mémoire scratch cells
+
+ Enable the optional aid-mémoire in Play for a non-constraining row
+ or compact grid of scratch cells. Each cell has its own label and
+ accepts values, corner marks, centre marks and colours through the
+ regular keypad or keyboard. Arrow keys stay within the configured
+ grid; Home/End move within a row, Ctrl/⌘ + Home/End reach the first
+ or last cell, and Page Up/Down move to the first or last row. The
+ layout and entries are preserved with local Library progress.
+
+
+
+
Variant and false clues
+
+ The setter supports cages, lines, pair clues, X-sums, skyscrapers,
+ quadruples and maximum cells. Enable Wrogn mode to make new local
+ clues false, or switch existing clues individually or as a batch.
+ Red dashed artwork and a ≠ mark identify false clues; Σ and ▥
+ identify X-sum and skyscraper readings. A false multi-cell clue
+ often stays undecided until enough of its cells are known, so exact
+ searches for dense liar puzzles can be substantially slower.
+
+
Generation and ratings
Generation runs in a worker with explicit time and search limits. A
requested level guides clue removal; the reported rating is then
calculated independently from logical techniques, clue load and
- bounded exact-search evidence. A limit never becomes a false
+ bounded exact-search evidence. Practice mode deterministically mines
+ several candidates and succeeds only when the analysed solve path
+ contains the requested technique. A limit never becomes a false
uniqueness claim.
@@ -78,10 +140,21 @@ export function HelpDialog({
Import and privacy
Files, text, solving and generation stay in this browser. Compact
- grids, project JSON, share fragments and supported f-puzzles data
- are decoded locally. SudokuPad short IDs need its server and are
- intentionally rejected. Review an export before sharing: titles,
- authors, rules, solutions and progress may be included.
+ grids, project JSON, share fragments, supported f-puzzles,
+ SudokuPad/CTC inline data and supported Penpa+ long links are
+ decoded locally. Server short IDs are intentionally rejected. SVG,
+ PNG and PDF rendering also stays in the browser. Review an export
+ before sharing: titles, authors, rules, solutions, progress and
+ aid-mémoire entries may be included.
+
+
+
+
Screen-reader detail
+
+ Each Sudoku cell reports its row, column, region, value or notes,
+ colour, conflict state, candidate highlights and touching variant
+ clues. The board and aid-mémoire use real row and gridcell roles;
+ their keyboard instructions are attached to the grids.
+ {sumLab.result.truncated
+ ? `Analysis reached its ${sumLab.result.truncationReason ?? "safety"} limit. `
+ : ""}
+ The visible list is bounded; narrow the range or add filters
+ before making deductions from an incomplete result.
+
- Accepts a plain grid, Sudoku Tools JSON/share link, raw f-puzzles
- JSON, or a self-contained f-puzzles/SudokuPad link. Server-only
- short IDs are never fetched.
+ Accepts a plain grid, Sudoku Tools JSON/share link, f-puzzles or
+ SudokuPad/CTC data, and supported Penpa+ long links. Everything is
+ decoded locally. Server-only short IDs are never fetched.
+ Unsupported constructs stop the import instead of being discarded.