feat: complete advanced Sudoku workbench

This commit is contained in:
2026-08-31 08:20:30 +02:00
parent 8ca9300ab3
commit 0a1bdc1a8c
99 changed files with 20793 additions and 923 deletions
+59 -2
View File
@@ -115,6 +115,63 @@ describe("fpuzzles interoperability", () => {
expect(imported.constraints).toEqual(expect.arrayContaining(expected));
});
it("round-trips the registered expansion-pack constraints", () => {
const solution = [
1, 2, 3, 4, 5, 6, 7, 8, 9, 4, 5, 6, 7, 8, 9, 1, 2, 3, 7, 8, 9, 1, 2, 3, 4,
5, 6, 2, 3, 4, 5, 6, 7, 8, 9, 1, 5, 6, 7, 8, 9, 1, 2, 3, 4, 8, 9, 1, 2, 3,
4, 5, 6, 7, 3, 4, 5, 6, 7, 8, 9, 1, 2, 6, 7, 8, 9, 1, 2, 3, 4, 5, 9, 1, 2,
3, 4, 5, 6, 7, 8,
];
const source: SudokuDocument = {
schema: SUDOKU_DOCUMENT_SCHEMA,
version: 1,
size: 9,
givens: Array<number>(81).fill(0),
solution,
constraints: [
{ type: "disjoint-groups" },
{ type: "minimum", cell: 0 },
{ type: "odd", cell: 1 },
{ type: "even", cell: 2 },
{
type: "little-killer",
side: "top",
index: 0,
direction: "down-right",
sum: 45,
},
{ type: "sandwich", side: "left", index: 0, sum: 20 },
{ type: "between-line", cells: [0, 1, 2] },
{ type: "german-whisper", cells: [9, 10], minimumDifference: 4 },
{ type: "region-sum-line", cells: [18, 19, 20, 21] },
{ type: "clone", cells: [27, 28], cloneCells: [36, 37] },
{
type: "extra-region",
cells: [0, 10, 20, 30, 40, 50, 60, 70, 80],
},
{ type: "modular-line", cells: [45, 46, 47] },
{ type: "entropic-line", cells: [54, 55, 56] },
{ type: "zipper-line", cells: [63, 64, 65] },
{ type: "double-arrow", cells: [72, 73, 74] },
{ type: "indexer", kind: "row", cell: 3 },
{ type: "indexer", kind: "column", cell: 4 },
{ type: "indexer", kind: "box", cell: 5 },
{ type: "fog", lights: [0, 40], revealRadius: 1 },
],
};
const imported = parseFpuzzles(exportFpuzzles(source));
expect(imported.constraints).toEqual(
expect.arrayContaining(
source.constraints.map((constraint) =>
constraint.type === "fog"
? { type: "fog", lights: constraint.lights }
: constraint,
),
),
);
});
it("recognizes server-only short puzzle IDs without making a request", () => {
expect(() => importFpuzzles("https://sudokupad.app/abc123")).toThrowError(
NetworkPuzzleIdError,
@@ -220,9 +277,9 @@ describe("fpuzzles interoperability", () => {
parseFpuzzles({
size: 9,
grid: emptyGrid(),
odd: [{ cell: "R1C1" }],
nabner: [{ lines: [["R1C1", "R1C2"]] }],
}),
).toThrow(/odd cells.*silently weakening/u);
).toThrow(/Nabner lines.*silently weakening/u);
expect(() =>
parseFpuzzles({
size: 9,