logic-puzzle-generator 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +221 -0
  3. package/dist/Clue.d.ts +81 -0
  4. package/dist/Clue.js +37 -0
  5. package/dist/Generator.d.ts +58 -0
  6. package/dist/Generator.js +433 -0
  7. package/dist/LogicGrid.d.ts +70 -0
  8. package/dist/LogicGrid.js +188 -0
  9. package/dist/Solver.d.ts +29 -0
  10. package/dist/Solver.js +242 -0
  11. package/dist/examples/benchmark.d.ts +1 -0
  12. package/dist/examples/benchmark.js +129 -0
  13. package/dist/examples/cli.d.ts +1 -0
  14. package/dist/examples/cli.js +84 -0
  15. package/dist/index.d.ts +5 -0
  16. package/dist/index.js +21 -0
  17. package/dist/run_generator.d.ts +1 -0
  18. package/dist/run_generator.js +84 -0
  19. package/dist/src/defaults.d.ts +5 -0
  20. package/dist/src/defaults.js +24 -0
  21. package/dist/src/engine/BoundsCalculator.d.ts +6 -0
  22. package/dist/src/engine/BoundsCalculator.js +40 -0
  23. package/dist/src/engine/Clue.d.ts +75 -0
  24. package/dist/src/engine/Clue.js +10 -0
  25. package/dist/src/engine/DifficultyBounds.d.ts +12 -0
  26. package/dist/src/engine/DifficultyBounds.js +67 -0
  27. package/dist/src/engine/GenerativeSession.d.ts +32 -0
  28. package/dist/src/engine/GenerativeSession.js +109 -0
  29. package/dist/src/engine/Generator.d.ts +119 -0
  30. package/dist/src/engine/Generator.js +1058 -0
  31. package/dist/src/engine/LogicGrid.d.ts +70 -0
  32. package/dist/src/engine/LogicGrid.js +190 -0
  33. package/dist/src/engine/Solver.d.ts +30 -0
  34. package/dist/src/engine/Solver.js +613 -0
  35. package/dist/src/errors.d.ts +12 -0
  36. package/dist/src/errors.js +23 -0
  37. package/dist/src/index.d.ts +8 -0
  38. package/dist/src/index.js +24 -0
  39. package/dist/src/scripts/GenerateBoundsDeprecated.d.ts +1 -0
  40. package/dist/src/scripts/GenerateBoundsDeprecated.js +27 -0
  41. package/dist/src/scripts/StressTestBacktracking.d.ts +1 -0
  42. package/dist/src/scripts/StressTestBacktracking.js +49 -0
  43. package/dist/src/types.d.ts +86 -0
  44. package/dist/src/types.js +58 -0
  45. package/dist/types.d.ts +37 -0
  46. package/dist/types.js +13 -0
  47. package/package.json +40 -0
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./engine/Clue"), exports);
19
+ __exportStar(require("./engine/LogicGrid"), exports);
20
+ __exportStar(require("./engine/Solver"), exports);
21
+ __exportStar(require("./engine/Generator"), exports);
22
+ __exportStar(require("./engine/GenerativeSession"), exports);
23
+ __exportStar(require("./defaults"), exports);
24
+ __exportStar(require("./errors"), exports);
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const BoundsCalculator_1 = require("../engine/BoundsCalculator");
4
+ describe('Bounds Generation', () => {
5
+ // We increase timeout significantly for simulation
6
+ jest.setTimeout(120000);
7
+ test('Generate Table', () => {
8
+ const configs = [
9
+ { c: 2, i: 3 }, { c: 2, i: 4 }, { c: 2, i: 5 }, { c: 2, i: 6 },
10
+ { c: 3, i: 3 }, { c: 3, i: 4 }, { c: 3, i: 5 }, { c: 3, i: 6 },
11
+ { c: 4, i: 3 }, { c: 4, i: 4 }, { c: 4, i: 5 }, { c: 4, i: 6 }
12
+ ];
13
+ const results = {};
14
+ console.log("Generating Bounds Table (this may take a minute)...");
15
+ for (const conf of configs) {
16
+ const key = `${conf.c}x${conf.i}`;
17
+ // 20 iterations per config
18
+ process.stdout.write(`Computing ${key}... `);
19
+ const bounds = BoundsCalculator_1.BoundsCalculator.calculate(conf.c, conf.i, 20);
20
+ results[key] = bounds;
21
+ console.log(`[${bounds.min}, ${bounds.max}]`);
22
+ }
23
+ console.log("\n--- JSON OUTPUT ---");
24
+ console.log(JSON.stringify(results, null, 2));
25
+ console.log("--- END JSON ---\n");
26
+ });
27
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const Generator_1 = require("../engine/Generator");
4
+ const types_1 = require("../types");
5
+ // CONFIGURATION
6
+ const NUM_CATS = 5;
7
+ const NUM_ITEMS = 10;
8
+ const TARGET_CLUES = 45; // Minimum is 35, so 45 is still very hard (tight constraint)
9
+ const TIMEOUT_MS = 60000; // Give it 60s for this extreme test
10
+ // Setup Categories
11
+ const categories = [];
12
+ for (let i = 0; i < NUM_CATS; i++) {
13
+ const values = [];
14
+ for (let j = 0; j < NUM_ITEMS; j++) {
15
+ values.push(`Item ${j + 1}`);
16
+ }
17
+ categories.push({
18
+ id: `cat_${i + 1}`,
19
+ type: types_1.CategoryType.NOMINAL,
20
+ values: values
21
+ });
22
+ }
23
+ const generator = new Generator_1.Generator(Date.now()); // Seed with time
24
+ console.log(`Starting Stress Test: ${NUM_CATS}x${NUM_ITEMS} Grid. Target: ${TARGET_CLUES}`);
25
+ let lastLogTime = 0;
26
+ try {
27
+ const puzzle = generator.generatePuzzle(categories, undefined, {
28
+ maxCandidates: Infinity,
29
+ targetClueCount: TARGET_CLUES,
30
+ timeoutMs: TIMEOUT_MS,
31
+ onTrace: (msg) => {
32
+ // Throttle logs to avoid flooding terminal
33
+ if (msg.includes("Depth") || msg.includes("SOLVED") || msg.includes("Pruning")) {
34
+ console.log(msg);
35
+ }
36
+ else if (Date.now() - lastLogTime > 1000) {
37
+ console.log(msg);
38
+ lastLogTime = Date.now();
39
+ }
40
+ }
41
+ });
42
+ console.log("SUCCESS! Puzzle generated.");
43
+ console.log(`Clues: ${puzzle.clues.length}`);
44
+ console.log("Proof Chain Length:", puzzle.proofChain.length);
45
+ }
46
+ catch (error) {
47
+ console.error("FAILED to generate puzzle.");
48
+ console.error(error);
49
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * A primitive value that acts as a label for an entity (e.g., "Alice", 42).
3
+ */
4
+ export type ValueLabel = string | number;
5
+ /**
6
+ * Defines the nature of a category's values.
7
+ */
8
+ export declare enum CategoryType {
9
+ /** Order is irrelevant (e.g., Name, Genre). */
10
+ NOMINAL = 0,
11
+ /** Order is crucial for comparisons (e.g., Age, Price). Values must be numbers. */
12
+ ORDINAL = 1
13
+ }
14
+ /**
15
+ * Configuration for a single category in the puzzle.
16
+ */
17
+ export interface CategoryConfig {
18
+ /** Unique internal identifier for the category (e.g., 'Name'). */
19
+ id: string;
20
+ /** The type of data this category holds. */
21
+ type: CategoryType;
22
+ /** The list of possible values. Must be unique. If ORDINAL, they must be sorted. */
23
+ values: ValueLabel[];
24
+ }
25
+ /**
26
+ * Represents the complete solution to the puzzle (the "answer key").
27
+ * Structure: { CategoryID -> { Value -> CorrectValueInOtherCategory } }
28
+ */
29
+ export type Solution = Record<string, Record<string, ValueLabel>>;
30
+ /**
31
+ * A specific correlation that the puzzle solver aims to deduce as the final answer.
32
+ */
33
+ export interface TargetFact {
34
+ category1Id: string;
35
+ value1: ValueLabel;
36
+ category2Id: string;
37
+ }
38
+ /**
39
+ * Enumeration of all supported clue types.
40
+ */
41
+ export declare enum ClueType {
42
+ /** Expresses a direct relationship (IS or IS NOT) between two values. */
43
+ BINARY = 0,
44
+ /** Expresses a comparison (GREATER THAN or LESS THAN) between two values based on an ordinal category. */
45
+ ORDINAL = 1,
46
+ /** Expresses an extreme value relationship (MIN or MAX) within an ordinal category. */
47
+ SUPERLATIVE = 2,
48
+ /** Expresses a property of a single value (e.g., IS EVEN) relative to an ordinal category. */
49
+ UNARY = 3,
50
+ /** Expresses a relationship between relative positions in two different ordinal categories. */
51
+ CROSS_ORDINAL = 4
52
+ }
53
+ export declare enum CrossOrdinalOperator {
54
+ MATCH = 0,
55
+ NOT_MATCH = 1
56
+ }
57
+ export declare enum BinaryOperator {
58
+ IS = 0,
59
+ IS_NOT = 1
60
+ }
61
+ export declare enum OrdinalOperator {
62
+ GREATER_THAN = 0,
63
+ LESS_THAN = 1,
64
+ NOT_GREATER_THAN = 2,
65
+ NOT_LESS_THAN = 3
66
+ }
67
+ export declare enum SuperlativeOperator {
68
+ MIN = 0,
69
+ MAX = 1,
70
+ NOT_MIN = 2,
71
+ NOT_MAX = 3
72
+ }
73
+ export declare enum UnaryFilter {
74
+ IS_ODD = 0,
75
+ IS_EVEN = 1
76
+ }
77
+ /**
78
+ * Configuration for constraining the types of clues generated.
79
+ */
80
+ export interface ClueGenerationConstraints {
81
+ /**
82
+ * If provided, only clues of these types will be generated.
83
+ * Use this to control difficulty or puzzle attributes.
84
+ */
85
+ allowedClueTypes?: ClueType[];
86
+ }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.UnaryFilter = exports.SuperlativeOperator = exports.OrdinalOperator = exports.BinaryOperator = exports.CrossOrdinalOperator = exports.ClueType = exports.CategoryType = void 0;
4
+ /**
5
+ * Defines the nature of a category's values.
6
+ */
7
+ var CategoryType;
8
+ (function (CategoryType) {
9
+ /** Order is irrelevant (e.g., Name, Genre). */
10
+ CategoryType[CategoryType["NOMINAL"] = 0] = "NOMINAL";
11
+ /** Order is crucial for comparisons (e.g., Age, Price). Values must be numbers. */
12
+ CategoryType[CategoryType["ORDINAL"] = 1] = "ORDINAL";
13
+ })(CategoryType || (exports.CategoryType = CategoryType = {}));
14
+ /**
15
+ * Enumeration of all supported clue types.
16
+ */
17
+ var ClueType;
18
+ (function (ClueType) {
19
+ /** Expresses a direct relationship (IS or IS NOT) between two values. */
20
+ ClueType[ClueType["BINARY"] = 0] = "BINARY";
21
+ /** Expresses a comparison (GREATER THAN or LESS THAN) between two values based on an ordinal category. */
22
+ ClueType[ClueType["ORDINAL"] = 1] = "ORDINAL";
23
+ /** Expresses an extreme value relationship (MIN or MAX) within an ordinal category. */
24
+ ClueType[ClueType["SUPERLATIVE"] = 2] = "SUPERLATIVE";
25
+ /** Expresses a property of a single value (e.g., IS EVEN) relative to an ordinal category. */
26
+ ClueType[ClueType["UNARY"] = 3] = "UNARY";
27
+ /** Expresses a relationship between relative positions in two different ordinal categories. */
28
+ ClueType[ClueType["CROSS_ORDINAL"] = 4] = "CROSS_ORDINAL";
29
+ })(ClueType || (exports.ClueType = ClueType = {}));
30
+ var CrossOrdinalOperator;
31
+ (function (CrossOrdinalOperator) {
32
+ CrossOrdinalOperator[CrossOrdinalOperator["MATCH"] = 0] = "MATCH";
33
+ CrossOrdinalOperator[CrossOrdinalOperator["NOT_MATCH"] = 1] = "NOT_MATCH";
34
+ })(CrossOrdinalOperator || (exports.CrossOrdinalOperator = CrossOrdinalOperator = {}));
35
+ var BinaryOperator;
36
+ (function (BinaryOperator) {
37
+ BinaryOperator[BinaryOperator["IS"] = 0] = "IS";
38
+ BinaryOperator[BinaryOperator["IS_NOT"] = 1] = "IS_NOT";
39
+ })(BinaryOperator || (exports.BinaryOperator = BinaryOperator = {}));
40
+ var OrdinalOperator;
41
+ (function (OrdinalOperator) {
42
+ OrdinalOperator[OrdinalOperator["GREATER_THAN"] = 0] = "GREATER_THAN";
43
+ OrdinalOperator[OrdinalOperator["LESS_THAN"] = 1] = "LESS_THAN";
44
+ OrdinalOperator[OrdinalOperator["NOT_GREATER_THAN"] = 2] = "NOT_GREATER_THAN";
45
+ OrdinalOperator[OrdinalOperator["NOT_LESS_THAN"] = 3] = "NOT_LESS_THAN";
46
+ })(OrdinalOperator || (exports.OrdinalOperator = OrdinalOperator = {}));
47
+ var SuperlativeOperator;
48
+ (function (SuperlativeOperator) {
49
+ SuperlativeOperator[SuperlativeOperator["MIN"] = 0] = "MIN";
50
+ SuperlativeOperator[SuperlativeOperator["MAX"] = 1] = "MAX";
51
+ SuperlativeOperator[SuperlativeOperator["NOT_MIN"] = 2] = "NOT_MIN";
52
+ SuperlativeOperator[SuperlativeOperator["NOT_MAX"] = 3] = "NOT_MAX";
53
+ })(SuperlativeOperator || (exports.SuperlativeOperator = SuperlativeOperator = {}));
54
+ var UnaryFilter;
55
+ (function (UnaryFilter) {
56
+ UnaryFilter[UnaryFilter["IS_ODD"] = 0] = "IS_ODD";
57
+ UnaryFilter[UnaryFilter["IS_EVEN"] = 1] = "IS_EVEN";
58
+ })(UnaryFilter || (exports.UnaryFilter = UnaryFilter = {}));
@@ -0,0 +1,37 @@
1
+ /**
2
+ * A primitive value that acts as a label for an entity (e.g., "Alice", 42).
3
+ */
4
+ export type ValueLabel = string | number;
5
+ /**
6
+ * Defines the nature of a category's values.
7
+ */
8
+ export declare enum CategoryType {
9
+ /** Order is irrelevant (e.g., Name, Genre). */
10
+ NOMINAL = 0,
11
+ /** Order is crucial for comparisons (e.g., Age, Price). Values must be numbers. */
12
+ ORDINAL = 1
13
+ }
14
+ /**
15
+ * Configuration for a single category in the puzzle.
16
+ */
17
+ export interface CategoryConfig {
18
+ /** Unique internal identifier for the category (e.g., 'Name'). */
19
+ id: string;
20
+ /** The type of data this category holds. */
21
+ type: CategoryType;
22
+ /** The list of possible values. Must be unique. If ORDINAL, they must be sorted. */
23
+ values: ValueLabel[];
24
+ }
25
+ /**
26
+ * Represents the complete solution to the puzzle (the "answer key").
27
+ * Structure: { CategoryID -> { Value -> CorrectValueInOtherCategory } }
28
+ */
29
+ export type Solution = Record<string, Record<string, ValueLabel>>;
30
+ /**
31
+ * A specific correlation that the puzzle solver aims to deduce as the final answer.
32
+ */
33
+ export interface TargetFact {
34
+ category1Id: string;
35
+ value1: ValueLabel;
36
+ category2Id: string;
37
+ }
package/dist/types.js ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CategoryType = void 0;
4
+ /**
5
+ * Defines the nature of a category's values.
6
+ */
7
+ var CategoryType;
8
+ (function (CategoryType) {
9
+ /** Order is irrelevant (e.g., Name, Genre). */
10
+ CategoryType[CategoryType["NOMINAL"] = 0] = "NOMINAL";
11
+ /** Order is crucial for comparisons (e.g., Age, Price). Values must be numbers. */
12
+ CategoryType[CategoryType["ORDINAL"] = 1] = "ORDINAL";
13
+ })(CategoryType || (exports.CategoryType = CategoryType = {}));
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "logic-puzzle-generator",
3
+ "version": "1.0.0",
4
+ "description": "A headless, TypeScript-based Logic Grid Puzzle Engine.",
5
+ "main": "dist/src/index.js",
6
+ "types": "dist/src/index.d.ts",
7
+ "files": [
8
+ "dist"
9
+ ],
10
+ "scripts": {
11
+ "test": "jest",
12
+ "build": "tsc",
13
+ "prepublishOnly": "npm run build && npm test",
14
+ "start": "node dist/examples/cli.js",
15
+ "benchmark": "tsc && node dist/examples/benchmark.js",
16
+ "release": "standard-version",
17
+ "prepare": "husky"
18
+ },
19
+ "keywords": [
20
+ "logic",
21
+ "puzzle",
22
+ "generator",
23
+ "zebra puzzle",
24
+ "grid",
25
+ "typescript"
26
+ ],
27
+ "author": "",
28
+ "license": "MIT",
29
+ "devDependencies": {
30
+ "@commitlint/cli": "^20.2.0",
31
+ "@commitlint/config-conventional": "^20.2.0",
32
+ "@types/jest": "^29.5.12",
33
+ "@types/node": "^20.12.7",
34
+ "husky": "^9.1.7",
35
+ "jest": "^29.7.0",
36
+ "standard-version": "^9.5.0",
37
+ "ts-jest": "^29.1.2",
38
+ "typescript": "^5.4.5"
39
+ }
40
+ }