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
+27 -17
View File
@@ -2,7 +2,6 @@ import { compressToBase64 } from "lz-string";
import { describe, expect, it, vi } from "vitest";
import {
NetworkPuzzleIdError,
UnsupportedPuzzleConstructsError,
importPenpa,
importPuzzle,
importSudokuPad,
@@ -91,27 +90,38 @@ describe("local puzzle interoperability", () => {
expect(detected.document.givens[0]).toBe(1);
});
it("rejects visual-only SCL constructs instead of weakening them", () => {
expect(() =>
parseSudokuPadPuzzle({
...sclPuzzle(),
lines: [
{
wayPoints: [
[0.5, 0.5],
[1.5, 1.5],
],
},
],
overlays: [{ text: "?" }],
}),
).toThrow(UnsupportedPuzzleConstructsError);
it("preserves allowlisted SCL drawings without treating them as rules", async () => {
const source = {
...sclPuzzle(),
lines: [
{
wayPoints: [
[0.5, 0.5],
[1.5, 1.5],
],
},
],
overlays: [{ center: [1, 1], width: 1, height: 1, text: "?" }],
};
const parsed = parseSudokuPadPuzzle(source);
const imported = await importPuzzle(JSON.stringify(source));
expect(parsed.visuals).toHaveLength(3);
expect(parsed.source).toEqual({ format: "sudokupad", id: "local-scl" });
expect(imported.preview.preservedVisuals).toEqual(
expect.arrayContaining([
expect.objectContaining({ key: "overlay:polyline", count: 1 }),
expect.objectContaining({ key: "overlay:text", count: 1 }),
]),
);
expect(imported.preview.warnings.join(" ")).toMatch(/not solver-enforced/u);
expect(() =>
parseSudokuPadPuzzle({
...sclPuzzle(),
lines: [{}],
}),
).toThrow(/visual lines/u);
).toThrow(/wayPoints|bounded/u);
});
it("parses semantic Penpa+ Sudoku layers and local progress", () => {