feat: complete advanced Sudoku workbench
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
CONSTRAINT_REGISTRY,
|
||||
CONSTRAINT_TYPES,
|
||||
constraintAllowedFields,
|
||||
constraintCells,
|
||||
constraintLabel,
|
||||
constraintMetadata,
|
||||
isConstraintType,
|
||||
type ConstraintType,
|
||||
} from "../../src/domain";
|
||||
|
||||
const expectedTypes = [
|
||||
"diagonal",
|
||||
"anti-knight",
|
||||
"anti-king",
|
||||
"non-consecutive",
|
||||
"disjoint-groups",
|
||||
"killer-cage",
|
||||
"thermo",
|
||||
"arrow",
|
||||
"kropki",
|
||||
"xv",
|
||||
"inequality",
|
||||
"renban",
|
||||
"palindrome",
|
||||
"x-sum",
|
||||
"skyscraper",
|
||||
"quadruple",
|
||||
"maximum",
|
||||
"minimum",
|
||||
"odd",
|
||||
"even",
|
||||
"little-killer",
|
||||
"sandwich",
|
||||
"between-line",
|
||||
"german-whisper",
|
||||
"region-sum-line",
|
||||
"clone",
|
||||
"extra-region",
|
||||
"modular-line",
|
||||
"entropic-line",
|
||||
"zipper-line",
|
||||
"double-arrow",
|
||||
"indexer",
|
||||
"fog",
|
||||
] as const satisfies readonly ConstraintType[];
|
||||
|
||||
describe("constraint registry", () => {
|
||||
it("contains one complete metadata entry for every constraint type", () => {
|
||||
expect([...CONSTRAINT_TYPES].sort()).toEqual([...expectedTypes].sort());
|
||||
expect(Object.keys(CONSTRAINT_REGISTRY).sort()).toEqual(
|
||||
[...expectedTypes].sort(),
|
||||
);
|
||||
for (const type of expectedTypes) {
|
||||
const metadata = constraintMetadata(type);
|
||||
expect(metadata.type).toBe(type);
|
||||
expect(metadata.label.length).toBeGreaterThan(0);
|
||||
expect(metadata.fields[0]).toEqual({
|
||||
key: "type",
|
||||
kind: "discriminator",
|
||||
required: true,
|
||||
});
|
||||
expect(new Set(metadata.fields.map(({ key }) => key)).size).toBe(
|
||||
metadata.fields.length,
|
||||
);
|
||||
expect(constraintAllowedFields(type).has("negated")).toBe(
|
||||
metadata.negatable,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("provides labels, field metadata and a safe type guard", () => {
|
||||
expect(constraintLabel("little-killer")).toBe("Little killer");
|
||||
expect(constraintLabel("future-rule")).toBe("Future Rule");
|
||||
expect(isConstraintType("sandwich")).toBe(true);
|
||||
expect(isConstraintType("not-a-rule")).toBe(false);
|
||||
expect(constraintAllowedFields("little-killer")).toEqual(
|
||||
new Set(["type", "side", "index", "direction", "sum", "negated"]),
|
||||
);
|
||||
});
|
||||
|
||||
it("resolves global, line, outside and local footprints centrally", () => {
|
||||
expect(constraintCells(4, { type: "diagonal", direction: "anti" })).toEqual(
|
||||
[3, 6, 9, 12],
|
||||
);
|
||||
expect(constraintCells(4, { type: "disjoint-groups" })).toHaveLength(16);
|
||||
expect(constraintCells(4, { type: "minimum", cell: 5 })).toEqual([
|
||||
5, 1, 9, 4, 6,
|
||||
]);
|
||||
expect(constraintCells(4, { type: "odd", cell: 5 })).toEqual([5]);
|
||||
expect(
|
||||
constraintCells(4, {
|
||||
type: "little-killer",
|
||||
side: "top",
|
||||
index: 0,
|
||||
direction: "down-right",
|
||||
sum: 10,
|
||||
}),
|
||||
).toEqual([0, 5, 10, 15]);
|
||||
expect(
|
||||
constraintCells(4, {
|
||||
type: "sandwich",
|
||||
side: "right",
|
||||
index: 2,
|
||||
sum: 3,
|
||||
}),
|
||||
).toEqual([11, 10, 9, 8]);
|
||||
expect(
|
||||
constraintCells(4, {
|
||||
type: "clone",
|
||||
cells: [0, 1],
|
||||
cloneCells: [10, 11],
|
||||
}),
|
||||
).toEqual([0, 1, 10, 11]);
|
||||
expect(
|
||||
constraintCells(4, { type: "indexer", kind: "box", cell: 0 }),
|
||||
).toEqual([0, 2, 8, 10]);
|
||||
expect(
|
||||
constraintCells(4, { type: "fog", lights: [0, 5], revealRadius: 1 }),
|
||||
).toEqual([0, 5]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user