logic-puzzle-generator 1.0.0 → 1.2.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.
package/dist/LogicGrid.js DELETED
@@ -1,188 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LogicGrid = void 0;
4
- /**
5
- * Represents the state of the logic puzzle grid.
6
- *
7
- * It manages the possibilities between every pair of values across different categories.
8
- * The grid is initialized where all connections are possible (true).
9
- * As clues are applied, possibilities are eliminated (set to false).
10
- */
11
- class LogicGrid {
12
- /**
13
- * Creates a new LogicGrid instance.
14
- *
15
- * @param categories - The configuration of categories and their values for the puzzle.
16
- * @throws {Error} If the configuration is invalid (duplicate IDs, duplicate values, or mismatched value counts).
17
- */
18
- constructor(categories) {
19
- this._validateCategories(categories);
20
- this.categories = categories;
21
- this.valueMap = new Map(categories.map(c => [c.id, new Map(c.values.map((v, i) => [v, i]))]));
22
- this.grid = new Map();
23
- for (const cat1 of categories) {
24
- const cat1Map = new Map();
25
- for (const val1 of cat1.values) {
26
- const cat2Map = new Map();
27
- for (const cat2 of categories) {
28
- if (cat1.id === cat2.id)
29
- continue;
30
- cat2Map.set(cat2.id, Array(cat2.values.length).fill(true));
31
- }
32
- cat1Map.set(val1, cat2Map);
33
- }
34
- this.grid.set(cat1.id, cat1Map);
35
- }
36
- }
37
- _validateCategories(categories) {
38
- if (categories.length === 0) {
39
- return; // Or throw an error, for now we allow it.
40
- }
41
- const firstCategoryValueCount = categories[0].values.length;
42
- const categoryIds = new Set();
43
- for (const category of categories) {
44
- if (categoryIds.has(category.id)) {
45
- throw new Error(`Invalid configuration: Duplicate category ID found: "${category.id}".`);
46
- }
47
- categoryIds.add(category.id);
48
- if (category.values.length !== firstCategoryValueCount) {
49
- throw new Error(`Invalid configuration: All categories must have the same number of values. Category "${category.id}" has ${category.values.length} values, but category "${categories[0].id}" has ${firstCategoryValueCount}.`);
50
- }
51
- const categoryValues = new Set();
52
- for (const value of category.values) {
53
- if (categoryValues.has(value)) {
54
- throw new Error(`Invalid configuration: Duplicate value "${value}" found in category "${category.id}".`);
55
- }
56
- categoryValues.add(value);
57
- }
58
- }
59
- }
60
- /**
61
- * Sets the possibility state between two values from different categories.
62
- *
63
- * @param cat1Id - The ID of the first category.
64
- * @param val1 - The value from the first category.
65
- * @param cat2Id - The ID of the second category.
66
- * @param val2 - The value from the second category.
67
- * @param state - true if the connection is possible, false if eliminated.
68
- */
69
- setPossibility(cat1Id, val1, cat2Id, val2, state) {
70
- const val2Index = this.valueMap.get(cat2Id)?.get(val2);
71
- if (val2Index !== undefined) {
72
- const cat1Map = this.grid.get(cat1Id);
73
- if (cat1Map) {
74
- const val1Map = cat1Map.get(val1);
75
- if (val1Map) {
76
- const cat2Arr = val1Map.get(cat2Id);
77
- if (cat2Arr) {
78
- cat2Arr[val2Index] = state;
79
- }
80
- }
81
- }
82
- }
83
- const val1Index = this.valueMap.get(cat1Id)?.get(val1);
84
- if (val1Index !== undefined) {
85
- const cat2Map = this.grid.get(cat2Id);
86
- if (cat2Map) {
87
- const val2Map = cat2Map.get(val2);
88
- if (val2Map) {
89
- const cat1Arr = val2Map.get(cat1Id);
90
- if (cat1Arr) {
91
- cat1Arr[val1Index] = state;
92
- }
93
- }
94
- }
95
- }
96
- }
97
- /**
98
- * Checks if a connection between two values is currently possible.
99
- *
100
- * @param cat1Id - The ID of the first category.
101
- * @param val1 - The value from the first category.
102
- * @param cat2Id - The ID of the second category.
103
- * @param val2 - The value from the second category.
104
- * @returns true if the connection is possible, false otherwise.
105
- */
106
- isPossible(cat1Id, val1, cat2Id, val2) {
107
- const val2Index = this.valueMap.get(cat2Id)?.get(val2);
108
- if (val2Index === undefined)
109
- return false;
110
- const cat1Grid = this.grid.get(cat1Id);
111
- if (!cat1Grid)
112
- return false;
113
- const val1Grid = cat1Grid.get(val1);
114
- if (!val1Grid)
115
- return false;
116
- const cat2Arr = val1Grid.get(cat2Id);
117
- if (!cat2Arr)
118
- return false;
119
- return cat2Arr[val2Index];
120
- }
121
- /**
122
- * Gets the number of possible connections for a specific value in one category
123
- * relative to another category.
124
- *
125
- * @param cat1Id - The ID of the starting category.
126
- * @param val1 - The value from the starting category.
127
- * @param cat2Id - The target category ID.
128
- * @returns The number of values in cat2 that are still possible for val1.
129
- */
130
- getPossibilitiesCount(cat1Id, val1, cat2Id) {
131
- const possibilities = this.grid.get(cat1Id)?.get(val1)?.get(cat2Id);
132
- if (!possibilities)
133
- return 0;
134
- return possibilities.filter(p => p).length;
135
- }
136
- /**
137
- * Calculates statistics about the current state of the grid.
138
- *
139
- * @returns An object containing:
140
- * - totalPossible: The initial total logical connections.
141
- * - currentPossible: The number of remaining possible connections.
142
- * - solutionPossible: The target number of connections for a solved grid.
143
- */
144
- getGridStats() {
145
- let currentPossible = 0;
146
- const catCount = this.categories.length;
147
- const valCount = this.categories[0]?.values.length || 0;
148
- if (valCount === 0 || catCount < 2) {
149
- return { totalPossible: 0, currentPossible: 0, solutionPossible: 0 };
150
- }
151
- const numPairs = catCount * (catCount - 1) / 2;
152
- const totalPossible = numPairs * valCount * valCount;
153
- const solutionPossible = numPairs * valCount;
154
- for (const cat1 of this.categories) {
155
- for (const val1 of cat1.values) {
156
- for (const cat2 of this.categories) {
157
- if (cat1.id >= cat2.id)
158
- continue;
159
- const possibilities = this.grid.get(cat1.id)?.get(val1)?.get(cat2.id);
160
- if (possibilities) {
161
- currentPossible += possibilities.filter(p => p).length;
162
- }
163
- }
164
- }
165
- }
166
- return { totalPossible, currentPossible, solutionPossible };
167
- }
168
- /**
169
- * Creates a deep copy of the current LogicGrid.
170
- *
171
- * @returns A new LogicGrid instance with the exact same state.
172
- */
173
- clone() {
174
- const newGrid = new LogicGrid(this.categories);
175
- newGrid.grid = new Map([...this.grid.entries()].map(([cat1Id, cat1Map]) => [
176
- cat1Id,
177
- new Map([...cat1Map.entries()].map(([val1, val1Map]) => [
178
- val1,
179
- new Map([...val1Map.entries()].map(([cat2Id, cat2Arr]) => [
180
- cat2Id,
181
- [...cat2Arr],
182
- ])),
183
- ])),
184
- ]));
185
- return newGrid;
186
- }
187
- }
188
- exports.LogicGrid = LogicGrid;
package/dist/Solver.d.ts DELETED
@@ -1,29 +0,0 @@
1
- import { LogicGrid } from './LogicGrid';
2
- import { Clue } from './Clue';
3
- /**
4
- * The logical engine responsible for applying clues to a LogicGrid and deducing consequences.
5
- *
6
- * It implements various deduction strategies including basic elimination, uniqueness checks,
7
- * and transitive logic logic (if A=B and B=C, then A=C).
8
- */
9
- export declare class Solver {
10
- /**
11
- * Applies a single clue to the grid and propagates logical deductions.
12
- *
13
- * This method runs a loop that explicitly applies the clue and then repeatedly
14
- * triggers the internal deduction engine until no further eliminations can be made.
15
- *
16
- * @param grid - The LogicGrid to modify.
17
- * @param clue - The Clue to apply.
18
- * @returns An object containing the modified grid and the total count of eliminations made.
19
- */
20
- applyClue(grid: LogicGrid, clue: Clue): {
21
- grid: LogicGrid;
22
- deductions: number;
23
- };
24
- private applyUnaryClue;
25
- private applyBinaryClue;
26
- private runDeductionLoop;
27
- private applyOrdinalClue;
28
- private applySuperlativeClue;
29
- }
package/dist/Solver.js DELETED
@@ -1,242 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Solver = void 0;
4
- const Clue_1 = require("./Clue");
5
- const types_1 = require("./types");
6
- /**
7
- * The logical engine responsible for applying clues to a LogicGrid and deducing consequences.
8
- *
9
- * It implements various deduction strategies including basic elimination, uniqueness checks,
10
- * and transitive logic logic (if A=B and B=C, then A=C).
11
- */
12
- class Solver {
13
- /**
14
- * Applies a single clue to the grid and propagates logical deductions.
15
- *
16
- * This method runs a loop that explicitly applies the clue and then repeatedly
17
- * triggers the internal deduction engine until no further eliminations can be made.
18
- *
19
- * @param grid - The LogicGrid to modify.
20
- * @param clue - The Clue to apply.
21
- * @returns An object containing the modified grid and the total count of eliminations made.
22
- */
23
- applyClue(grid, clue) {
24
- let deductions = 0;
25
- switch (clue.type) {
26
- case Clue_1.ClueType.BINARY:
27
- deductions += this.applyBinaryClue(grid, clue);
28
- break;
29
- case Clue_1.ClueType.SUPERLATIVE:
30
- deductions += this.applySuperlativeClue(grid, clue);
31
- break;
32
- case Clue_1.ClueType.ORDINAL:
33
- deductions += this.applyOrdinalClue(grid, clue);
34
- break;
35
- case Clue_1.ClueType.UNARY:
36
- deductions += this.applyUnaryClue(grid, clue);
37
- break;
38
- }
39
- let newDeductions;
40
- do {
41
- newDeductions = this.runDeductionLoop(grid);
42
- deductions += newDeductions;
43
- } while (newDeductions > 0);
44
- return { grid, deductions };
45
- }
46
- applyUnaryClue(grid, clue) {
47
- let deductions = 0;
48
- const categories = grid.categories;
49
- const ordinalCatConfig = categories.find(c => c.id === clue.ordinalCat);
50
- if (!ordinalCatConfig || ordinalCatConfig.type !== types_1.CategoryType.ORDINAL)
51
- return 0;
52
- if (!ordinalCatConfig.values.every(v => typeof v === 'number'))
53
- return 0;
54
- const isEven = clue.filter === Clue_1.UnaryFilter.IS_EVEN;
55
- for (const ordVal of ordinalCatConfig.values) {
56
- const ordNum = ordVal;
57
- const shouldEliminate = isEven ? ordNum % 2 !== 0 : ordNum % 2 === 0;
58
- if (shouldEliminate) {
59
- if (grid.isPossible(clue.targetCat, clue.targetVal, clue.ordinalCat, ordVal)) {
60
- grid.setPossibility(clue.targetCat, clue.targetVal, clue.ordinalCat, ordVal, false);
61
- deductions++;
62
- }
63
- }
64
- }
65
- return deductions;
66
- }
67
- applyBinaryClue(grid, clue) {
68
- let deductions = 0;
69
- const categories = grid.categories;
70
- const cat1Config = categories.find(c => c.id === clue.cat1);
71
- const cat2Config = categories.find(c => c.id === clue.cat2);
72
- if (!cat1Config || !cat2Config)
73
- return 0;
74
- if (clue.operator === Clue_1.BinaryOperator.IS) {
75
- if (grid.isPossible(clue.cat1, clue.val1, clue.cat2, clue.val2)) {
76
- // This is not a deduction, but a fact application. Still, we need to eliminate other possibilities.
77
- }
78
- grid.setPossibility(clue.cat1, clue.val1, clue.cat2, clue.val2, true);
79
- for (const val of cat2Config.values) {
80
- if (val !== clue.val2) {
81
- if (grid.isPossible(clue.cat1, clue.val1, clue.cat2, val)) {
82
- grid.setPossibility(clue.cat1, clue.val1, clue.cat2, val, false);
83
- deductions++;
84
- }
85
- }
86
- }
87
- for (const val of cat1Config.values) {
88
- if (val !== clue.val1) {
89
- if (grid.isPossible(clue.cat1, val, clue.cat2, clue.val2)) {
90
- grid.setPossibility(clue.cat1, val, clue.cat2, clue.val2, false);
91
- deductions++;
92
- }
93
- }
94
- }
95
- }
96
- else { // IS_NOT
97
- if (grid.isPossible(clue.cat1, clue.val1, clue.cat2, clue.val2)) {
98
- grid.setPossibility(clue.cat1, clue.val1, clue.cat2, clue.val2, false);
99
- deductions++;
100
- }
101
- }
102
- return deductions;
103
- }
104
- runDeductionLoop(grid) {
105
- let deductions = 0;
106
- const categories = grid.categories;
107
- for (const cat1 of categories) {
108
- for (const val1 of cat1.values) {
109
- for (const cat2 of categories) {
110
- if (cat1.id === cat2.id)
111
- continue;
112
- // Uniqueness Check
113
- const possibleValues = cat2.values.filter(val2 => grid.isPossible(cat1.id, val1, cat2.id, val2));
114
- if (possibleValues.length === 1) {
115
- const val2 = possibleValues[0];
116
- // If val1 is uniquely associated with val2, then no other val from cat1 can be associated with val2
117
- for (const otherVal1 of cat1.values) {
118
- if (otherVal1 !== val1) {
119
- if (grid.isPossible(cat1.id, otherVal1, cat2.id, val2)) {
120
- grid.setPossibility(cat1.id, otherVal1, cat2.id, val2, false);
121
- deductions++;
122
- }
123
- }
124
- }
125
- }
126
- // Transitivity
127
- for (const cat3 of categories) {
128
- if (cat1.id === cat3.id || cat2.id === cat3.id)
129
- continue;
130
- // Positive Transitivity
131
- const definiteVal2 = possibleValues.length === 1 ? possibleValues[0] : null;
132
- if (definiteVal2) {
133
- const possibleVal3s = cat3.values.filter(val3 => grid.isPossible(cat2.id, definiteVal2, cat3.id, val3));
134
- if (possibleVal3s.length === 1) {
135
- const definiteVal3 = possibleVal3s[0];
136
- if (grid.isPossible(cat1.id, val1, cat3.id, definiteVal3) === false) {
137
- // This indicates a contradiction, but for now we are just making deductions.
138
- }
139
- else if (grid.getPossibilitiesCount(cat1.id, val1, cat3.id) > 1) {
140
- grid.setPossibility(cat1.id, val1, cat3.id, definiteVal3, true);
141
- deductions++;
142
- }
143
- }
144
- }
145
- // Negative Transitivity
146
- for (const val3 of cat3.values) {
147
- if (grid.isPossible(cat1.id, val1, cat3.id, val3)) {
148
- const isPathPossible = cat2.values.some(val2 => grid.isPossible(cat1.id, val1, cat2.id, val2) && grid.isPossible(cat2.id, val2, cat3.id, val3));
149
- if (!isPathPossible) {
150
- grid.setPossibility(cat1.id, val1, cat3.id, val3, false);
151
- deductions++;
152
- }
153
- }
154
- }
155
- }
156
- }
157
- }
158
- }
159
- return deductions;
160
- }
161
- applyOrdinalClue(grid, constraint) {
162
- let deductions = 0;
163
- const categories = grid.categories;
164
- const ordCatConfig = categories.find(c => c.id === constraint.ordinalCat);
165
- if (!ordCatConfig || ordCatConfig.type !== types_1.CategoryType.ORDINAL)
166
- return 0;
167
- const possibleVals1 = ordCatConfig.values
168
- .map((v, i) => ({ val: v, idx: i }))
169
- .filter(v => grid.isPossible(constraint.item1Cat, constraint.item1Val, constraint.ordinalCat, v.val));
170
- const possibleVals2 = ordCatConfig.values
171
- .map((v, i) => ({ val: v, idx: i }))
172
- .filter(v => grid.isPossible(constraint.item2Cat, constraint.item2Val, constraint.ordinalCat, v.val));
173
- if (possibleVals1.length === 0 || possibleVals2.length === 0)
174
- return 0;
175
- if (constraint.operator === Clue_1.OrdinalOperator.GREATER_THAN) { // item1 > item2
176
- // Prune item1's possibilities
177
- for (const pval1 of possibleVals1) {
178
- const canBeGreaterThan = possibleVals2.some(pval2 => pval1.idx > pval2.idx);
179
- if (!canBeGreaterThan) {
180
- if (grid.isPossible(constraint.item1Cat, constraint.item1Val, constraint.ordinalCat, pval1.val)) {
181
- grid.setPossibility(constraint.item1Cat, constraint.item1Val, constraint.ordinalCat, pval1.val, false);
182
- deductions++;
183
- }
184
- }
185
- }
186
- // Prune item2's possibilities
187
- for (const pval2 of possibleVals2) {
188
- const canBeLessThan = possibleVals1.some(pval1 => pval1.idx > pval2.idx);
189
- if (!canBeLessThan) {
190
- if (grid.isPossible(constraint.item2Cat, constraint.item2Val, constraint.ordinalCat, pval2.val)) {
191
- grid.setPossibility(constraint.item2Cat, constraint.item2Val, constraint.ordinalCat, pval2.val, false);
192
- deductions++;
193
- }
194
- }
195
- }
196
- }
197
- else if (constraint.operator === Clue_1.OrdinalOperator.LESS_THAN) { // item1 < item2
198
- // Prune item1's possibilities
199
- for (const pval1 of possibleVals1) {
200
- const canBeLessThan = possibleVals2.some(pval2 => pval1.idx < pval2.idx);
201
- if (!canBeLessThan) {
202
- if (grid.isPossible(constraint.item1Cat, constraint.item1Val, constraint.ordinalCat, pval1.val)) {
203
- grid.setPossibility(constraint.item1Cat, constraint.item1Val, constraint.ordinalCat, pval1.val, false);
204
- deductions++;
205
- }
206
- }
207
- }
208
- // Prune item2's possibilities
209
- for (const pval2 of possibleVals2) {
210
- const canBeGreaterThan = possibleVals1.some(pval1 => pval1.idx < pval2.idx);
211
- if (!canBeGreaterThan) {
212
- if (grid.isPossible(constraint.item2Cat, constraint.item2Val, constraint.ordinalCat, pval2.val)) {
213
- grid.setPossibility(constraint.item2Cat, constraint.item2Val, constraint.ordinalCat, pval2.val, false);
214
- deductions++;
215
- }
216
- }
217
- }
218
- }
219
- return deductions;
220
- }
221
- applySuperlativeClue(grid, clue) {
222
- const categories = grid.categories;
223
- const ordinalCatConfig = categories.find(c => c.id === clue.ordinalCat);
224
- if (!ordinalCatConfig || ordinalCatConfig.type !== types_1.CategoryType.ORDINAL)
225
- return 0;
226
- // Assuming values are sorted in ascending order for ordinal categories
227
- const extremeValue = clue.operator === Clue_1.SuperlativeOperator.MAX
228
- ? ordinalCatConfig.values[ordinalCatConfig.values.length - 1]
229
- : ordinalCatConfig.values[0];
230
- // This clue is essentially a binary IS clue.
231
- const binaryClue = {
232
- type: Clue_1.ClueType.BINARY,
233
- cat1: clue.targetCat,
234
- val1: clue.targetVal,
235
- cat2: clue.ordinalCat,
236
- val2: extremeValue,
237
- operator: Clue_1.BinaryOperator.IS,
238
- };
239
- return this.applyBinaryClue(grid, binaryClue);
240
- }
241
- }
242
- exports.Solver = Solver;
package/dist/index.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export * from './types';
2
- export * from './Clue';
3
- export * from './LogicGrid';
4
- export * from './Solver';
5
- export * from './Generator';
package/dist/index.js DELETED
@@ -1,21 +0,0 @@
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("./Clue"), exports);
19
- __exportStar(require("./LogicGrid"), exports);
20
- __exportStar(require("./Solver"), exports);
21
- __exportStar(require("./Generator"), exports);
@@ -1 +0,0 @@
1
- export {};
@@ -1,84 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- const types_1 = require("./types");
4
- const Generator_1 = require("./Generator");
5
- const Solver_1 = require("./Solver");
6
- const LogicGrid_1 = require("./LogicGrid");
7
- const Clue_1 = require("./Clue");
8
- const categories = [
9
- { id: 'Name', type: types_1.CategoryType.NOMINAL, values: ['Alice', 'Bob', 'Charlie', 'David'] },
10
- { id: 'Genre', type: types_1.CategoryType.NOMINAL, values: ['Horror', 'Sci-Fi', 'Comedy', 'Drama'] },
11
- { id: 'Snack', type: types_1.CategoryType.NOMINAL, values: ['Chips', 'Popcorn', 'Candy', 'Chocolate'] },
12
- { id: 'Age', type: types_1.CategoryType.ORDINAL, values: [20, 30, 40, 50] },
13
- ];
14
- const targetFact = {
15
- category1Id: 'Name',
16
- value1: 'David',
17
- category2Id: 'Snack',
18
- };
19
- const seed = 1234;
20
- const generator = new Generator_1.Generator(seed);
21
- const puzzle = generator.generatePuzzle(categories, targetFact);
22
- console.log(`## ✨ Generated Puzzle (Seed: ${seed})`);
23
- console.log('\n---\n');
24
- console.log('### The Answer Key (For Verification Only)');
25
- const baseCat = puzzle.categories[0];
26
- for (const val of baseCat.values) {
27
- let line = '';
28
- for (const cat of puzzle.categories) {
29
- line += `${cat.id}: ${puzzle.solution[cat.id][val]} | `;
30
- }
31
- console.log(line.slice(0, -3));
32
- }
33
- console.log('\n### Goal');
34
- console.log(`What ${puzzle.targetFact.category2Id} does [Name: ${puzzle.targetFact.value1}] have?`);
35
- console.log('\n---\n');
36
- console.log('### 🧩 Clue-by-Clue Proof Chain\n');
37
- // The new API provides the proof chain directly, so we don't need to re-run the solver.
38
- // We still create a grid to show the ambiguity snapshot at each step.
39
- const logicGrid = new LogicGrid_1.LogicGrid(categories);
40
- const solver = new Solver_1.Solver();
41
- puzzle.proofChain.forEach((step, index) => {
42
- console.log(`STEP ${index + 1}: Clue: ${clueToString(step.clue)}`);
43
- console.log(`Deductions: ${step.deductions} eliminations made.`);
44
- // Apply the clue to our local grid to show the state *after* this clue is applied
45
- solver.applyClue(logicGrid, step.clue);
46
- console.log('\nAmbiguity Snapshot:');
47
- // Focused snapshot for the target fact
48
- const targetCat2 = categories.find(c => c.id === puzzle.targetFact.category2Id);
49
- if (targetCat2) {
50
- const possibleValues = targetCat2.values.filter(v => logicGrid.isPossible(puzzle.targetFact.category1Id, puzzle.targetFact.value1, puzzle.targetFact.category2Id, v));
51
- console.log(`- Target: [${puzzle.targetFact.category1Id}: ${puzzle.targetFact.value1}] vs [${puzzle.targetFact.category2Id}]: ${possibleValues.length} possibilities remain -> [${possibleValues.join(', ')}]`);
52
- }
53
- // Overall grid completion
54
- const { totalPossible, currentPossible, solutionPossible } = logicGrid.getGridStats();
55
- const totalEliminatable = totalPossible - solutionPossible;
56
- const eliminatedSoFar = totalPossible - currentPossible;
57
- const percentageSolved = totalEliminatable > 0 ? (eliminatedSoFar / totalEliminatable) * 100 : 0;
58
- console.log(`- Grid Solved: ${percentageSolved.toFixed(1)}%`);
59
- console.log('\n---\n');
60
- });
61
- const targetValue = puzzle.solution[puzzle.targetFact.category2Id][puzzle.targetFact.value1];
62
- console.log(`\n**Target Fact Solved:** [${puzzle.targetFact.category1Id}: ${puzzle.targetFact.value1}] is correlated with [${puzzle.targetFact.category2Id}: ${targetValue}]!`);
63
- function clueToString(clue) {
64
- switch (clue.type) {
65
- case Clue_1.ClueType.BINARY:
66
- const b = clue;
67
- const op = b.operator === Clue_1.BinaryOperator.IS ? 'is' : 'is not';
68
- return `[${b.cat1}: ${b.val1}] ${op} [${b.cat2}: ${b.val2}]`;
69
- case Clue_1.ClueType.ORDINAL:
70
- const o = clue;
71
- const ordOp = o.operator === Clue_1.OrdinalOperator.GREATER_THAN ? 'is greater than' : 'is less than';
72
- return `The one with [${o.item1Cat}: ${o.item1Val}] ${ordOp} the one with [${o.item2Cat}: ${o.item2Val}] (by ${o.ordinalCat})`;
73
- case Clue_1.ClueType.SUPERLATIVE:
74
- const s = clue;
75
- const supOp = s.operator === Clue_1.SuperlativeOperator.MAX ? 'is the oldest' : 'is the youngest';
76
- return `[${s.targetCat}: ${s.targetVal}] ${supOp}`;
77
- case Clue_1.ClueType.UNARY:
78
- const u = clue;
79
- const filterStr = u.filter === Clue_1.UnaryFilter.IS_EVEN ? 'even' : 'odd';
80
- return `[${u.targetCat}: ${u.targetVal}] is associated with an ${filterStr} ${u.ordinalCat}`;
81
- default:
82
- return 'Unknown Clue Type';
83
- }
84
- }
package/dist/types.d.ts DELETED
@@ -1,37 +0,0 @@
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 DELETED
@@ -1,13 +0,0 @@
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 = {}));