124 lines
3.2 KiB
TypeScript
124 lines
3.2 KiB
TypeScript
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]);
|
|
});
|
|
});
|