@logic-pad/core 0.5.0 → 0.6.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 (33) hide show
  1. package/assets/logic-core.global.d.ts +83 -22
  2. package/dist/data/grid.d.ts +44 -14
  3. package/dist/data/grid.js +43 -36
  4. package/dist/data/rules/banPatternRule.js +1 -1
  5. package/dist/data/rules/customRule.js +1 -1
  6. package/dist/data/rules/lyingSymbolRule.d.ts +1 -1
  7. package/dist/data/rules/lyingSymbolRule.js +38 -13
  8. package/dist/data/rules/musicGridRule.js +1 -1
  9. package/dist/data/rules/mysteryRule.js +1 -1
  10. package/dist/data/rules/regionAreaRule.js +3 -3
  11. package/dist/data/serializer/serializer_v0.js +1 -1
  12. package/dist/data/solver/allSolvers.js +2 -2
  13. package/dist/data/solver/backtrack/backtrackSolver.d.ts +3 -4
  14. package/dist/data/solver/backtrack/backtrackSolver.js +4 -30
  15. package/dist/data/solver/backtrack/backtrackWorker.d.ts +1 -2
  16. package/dist/data/solver/backtrack/backtrackWorker.js +12 -8
  17. package/dist/data/solver/eventIteratingSolver.d.ts +6 -0
  18. package/dist/data/solver/eventIteratingSolver.js +33 -0
  19. package/dist/data/solver/solver.d.ts +6 -1
  20. package/dist/data/solver/solver.js +2 -1
  21. package/dist/data/solver/universal/universalSolver.d.ts +7 -0
  22. package/dist/data/solver/universal/universalSolver.js +30 -0
  23. package/dist/data/solver/universal/universalWorker.d.ts +1 -0
  24. package/dist/data/solver/universal/universalWorker.js +119 -0
  25. package/dist/data/symbols/customIconSymbol.js +2 -2
  26. package/dist/data/symbols/customTextSymbol.js +2 -2
  27. package/dist/index.d.ts +3 -2
  28. package/dist/index.js +3 -2
  29. package/package.json +1 -1
  30. package/dist/data/solver/underclued/undercluedSolver.d.ts +0 -8
  31. package/dist/data/solver/underclued/undercluedSolver.js +0 -55
  32. package/dist/data/solver/underclued/undercluedWorker.d.ts +0 -2
  33. package/dist/data/solver/underclued/undercluedWorker.js +0 -135
@@ -1,4 +1,3 @@
1
- import { EventIterator } from 'event-iterator';
2
1
  import { instance as banPatternInstance } from '../../rules/banPatternRule.js';
3
2
  import { instance as cellCountInstance } from '../../rules/cellCountRule.js';
4
3
  import { instance as regionAreaInstance } from '../../rules/regionAreaRule.js';
@@ -6,7 +5,6 @@ import { instance as sameShapeInstance } from '../../rules/sameShapeRule.js';
6
5
  import { instance as symbolsPerRegionInstance } from '../../rules/symbolsPerRegionRule.js';
7
6
  import { instance as undercluedInstance } from '../../rules/undercluedRule.js';
8
7
  import { instance as uniqueShapeInstance } from '../../rules/uniqueShapeRule.js';
9
- import { Serializer } from '../../serializer/allSerializers.js';
10
8
  import { instance as areaNumberInstance } from '../../symbols/areaNumberSymbol.js';
11
9
  import { instance as dartInstance } from '../../symbols/dartSymbol.js';
12
10
  import { instance as galaxyInstance } from '../../symbols/galaxySymbol.js';
@@ -15,9 +13,9 @@ import { instance as lotusInstance } from '../../symbols/lotusSymbol.js';
15
13
  import { instance as minesweeperInstance } from '../../symbols/minesweeperSymbol.js';
16
14
  import { instance as myopiaInstance } from '../../symbols/myopiaSymbol.js';
17
15
  import { instance as viewpointInstance } from '../../symbols/viewpointSymbol.js';
18
- import Solver from '../solver.js';
19
16
  import { instance as connectAllInstance } from '../z3/modules/connectAllModule.js';
20
- class BacktrackSolver extends Solver {
17
+ import EventIteratingSolver from '../eventIteratingSolver.js';
18
+ class BacktrackSolver extends EventIteratingSolver {
21
19
  constructor() {
22
20
  super(...arguments);
23
21
  Object.defineProperty(this, "id", {
@@ -33,34 +31,10 @@ class BacktrackSolver extends Solver {
33
31
  value: 'Solves puzzles using backtracking with optimizations (blazingly fast). Support most rules and symbols (including underclued).'
34
32
  });
35
33
  }
36
- async *solve(grid) {
37
- const worker = new Worker(new URL(`./backtrackWorker.js`, import.meta.url), {
34
+ createWorker() {
35
+ return new Worker(new URL(`./backtrackWorker.js`, import.meta.url), {
38
36
  type: 'module',
39
37
  });
40
- try {
41
- const iterator = new EventIterator(({ push, stop, fail }) => {
42
- worker.postMessage(Serializer.stringifyGrid(grid.resetTiles()));
43
- worker.addEventListener('message', (e) => {
44
- if (e.data) {
45
- push(Serializer.parseGrid(e.data));
46
- }
47
- else {
48
- stop();
49
- }
50
- });
51
- worker.addEventListener('error', (e) => {
52
- alert(`Error while solving!\n${e.message}`);
53
- fail(e);
54
- });
55
- });
56
- for await (const solution of iterator) {
57
- yield solution;
58
- }
59
- yield null;
60
- }
61
- finally {
62
- worker.terminate();
63
- }
64
38
  }
65
39
  isInstructionSupported(instructionId) {
66
40
  return BacktrackSolver.supportedInstrs.includes(instructionId);
@@ -1,2 +1 @@
1
- declare const _default: null;
2
- export default _default;
1
+ export {};
@@ -74,9 +74,10 @@ function translateToBTGridData(grid) {
74
74
  else if (id === letterInstance.id) {
75
75
  continue;
76
76
  }
77
- if (!module)
77
+ if (!module && symbol.necessaryForCompletion)
78
78
  throw new Error('Symbol not supported.');
79
- modules.push(module);
79
+ if (module)
80
+ modules.push(module);
80
81
  }
81
82
  }
82
83
  const letterSymbols = grid.symbols.get(letterInstance.id);
@@ -226,7 +227,9 @@ function solveUnderclued(input) {
226
227
  function search(x, y, tile, color) {
227
228
  // count++;
228
229
  // console.log(`Trying (${x}, ${y}) with ${color}`);
229
- const newGrid = grid.setTile(x, y, tile.withColor(color));
230
+ const newGrid = grid.fastCopyWith({
231
+ tiles: grid.setTile(x, y, tile.withColor(color)),
232
+ });
230
233
  // Solve
231
234
  let solution;
232
235
  solveNormal(newGrid, sol => {
@@ -258,9 +261,13 @@ function solveUnderclued(input) {
258
261
  if (!darkPossible && !lightPossible)
259
262
  return null;
260
263
  if (darkPossible && !lightPossible)
261
- grid = grid.setTile(x, y, tile.withColor(Color.Dark));
264
+ grid = grid.fastCopyWith({
265
+ tiles: grid.setTile(x, y, tile.withColor(Color.Dark)),
266
+ });
262
267
  if (!darkPossible && lightPossible)
263
- grid = grid.setTile(x, y, tile.withColor(Color.Light));
268
+ grid = grid.fastCopyWith({
269
+ tiles: grid.setTile(x, y, tile.withColor(Color.Light)),
270
+ });
264
271
  }
265
272
  }
266
273
  // console.log(`Solve count: ${count}`);
@@ -289,6 +296,3 @@ onmessage = e => {
289
296
  // console.timeEnd('Solve time');
290
297
  postMessage(null);
291
298
  };
292
- // make typescript happy
293
- // eslint-disable-next-line import/no-anonymous-default-export
294
- export default null;
@@ -0,0 +1,6 @@
1
+ import GridData from '../grid.js';
2
+ import Solver, { CancelRef } from './solver.js';
3
+ export default abstract class EventIteratingSolver extends Solver {
4
+ protected abstract createWorker(): Worker;
5
+ solve(grid: GridData, cancelRef: CancelRef): AsyncGenerator<GridData | null>;
6
+ }
@@ -0,0 +1,33 @@
1
+ import { Serializer } from '../serializer/allSerializers.js';
2
+ import Solver from './solver.js';
3
+ import { EventIterator } from 'event-iterator';
4
+ export default class EventIteratingSolver extends Solver {
5
+ async *solve(grid, cancelRef) {
6
+ const worker = this.createWorker();
7
+ cancelRef.cancel = () => worker.terminate();
8
+ try {
9
+ const iterator = new EventIterator(({ push, stop, fail }) => {
10
+ worker.postMessage(Serializer.stringifyGrid(grid.resetTiles()));
11
+ worker.addEventListener('message', (e) => {
12
+ if (e.data) {
13
+ push(Serializer.parseGrid(e.data));
14
+ }
15
+ else {
16
+ stop();
17
+ }
18
+ });
19
+ worker.addEventListener('error', (e) => {
20
+ alert(`Error while solving!\n${e.message}`);
21
+ fail(e);
22
+ });
23
+ });
24
+ for await (const solution of iterator) {
25
+ yield solution;
26
+ }
27
+ yield null;
28
+ }
29
+ finally {
30
+ worker.terminate();
31
+ }
32
+ }
33
+ }
@@ -1,4 +1,7 @@
1
1
  import GridData from '../grid.js';
2
+ export interface CancelRef {
3
+ cancel?: () => void;
4
+ }
2
5
  /**
3
6
  * Base class that all solvers must extend.
4
7
  */
@@ -28,8 +31,10 @@ export default abstract class Solver {
28
31
  *
29
32
  * @param grid The grid to solve. The provided grid is guaranteed to be supported by the solver. Some tiles in the
30
33
  * grid may already be filled by the user. It is up to the solver to decide whether to respect these tiles or not.
34
+ * @param cancelRef A reference to a function that can be called to cancel the solver. If cancellation is supported,
35
+ * the solver can assign a function to `cancelRef.cancel` that will stop the solver when called.
31
36
  */
32
- abstract solve(grid: GridData): AsyncGenerator<GridData | null>;
37
+ abstract solve(grid: GridData, cancelRef: CancelRef): AsyncGenerator<GridData | null>;
33
38
  /**
34
39
  * Check if the solver supports the current browser environment. This method is called once when the user first clicks
35
40
  * the "Solve" button, and the result is cached for the duration of the editor session.
@@ -47,7 +47,8 @@ export default class Solver {
47
47
  if (grid.rules.some(rule => rule.necessaryForCompletion && !this.isInstructionSupported(rule.id))) {
48
48
  return false;
49
49
  }
50
- if ([...grid.symbols.keys()].some(id => !this.isInstructionSupported(id))) {
50
+ if ([...grid.symbols.keys()].some(id => grid.symbols.get(id)?.some(s => s.necessaryForCompletion) &&
51
+ !this.isInstructionSupported(id))) {
51
52
  return false;
52
53
  }
53
54
  return true;
@@ -0,0 +1,7 @@
1
+ import EventIteratingSolver from '../eventIteratingSolver.js';
2
+ export default class UniversalSolver extends EventIteratingSolver {
3
+ readonly id = "universal";
4
+ readonly description = "A backtracking solver that supports all rules and symbols (including underclued) but is less optimized.";
5
+ protected createWorker(): Worker;
6
+ isInstructionSupported(instructionId: string): boolean;
7
+ }
@@ -0,0 +1,30 @@
1
+ import { instance as undercluedInstance } from '../../rules/undercluedRule.js';
2
+ import EventIteratingSolver from '../eventIteratingSolver.js';
3
+ export default class UniversalSolver extends EventIteratingSolver {
4
+ constructor() {
5
+ super(...arguments);
6
+ Object.defineProperty(this, "id", {
7
+ enumerable: true,
8
+ configurable: true,
9
+ writable: true,
10
+ value: 'universal'
11
+ });
12
+ Object.defineProperty(this, "description", {
13
+ enumerable: true,
14
+ configurable: true,
15
+ writable: true,
16
+ value: 'A backtracking solver that supports all rules and symbols (including underclued) but is less optimized.'
17
+ });
18
+ }
19
+ createWorker() {
20
+ return new Worker(new URL('./universalWorker.js', import.meta.url), {
21
+ type: 'module',
22
+ });
23
+ }
24
+ isInstructionSupported(instructionId) {
25
+ if (super.isInstructionSupported(instructionId)) {
26
+ return true;
27
+ }
28
+ return instructionId === undercluedInstance.id;
29
+ }
30
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,119 @@
1
+ import { Color, State } from '../../primitives.js';
2
+ import { Serializer } from '../../serializer/allSerializers.js';
3
+ import { instance as undercluedInstance } from '../../rules/undercluedRule.js';
4
+ import { array } from '../../dataHelper.js';
5
+ import validateGrid from '../../validate.js';
6
+ function gridToRawTiles(grid) {
7
+ return array(grid.width, grid.height, (x, y) => grid.getTile(x, y).color);
8
+ }
9
+ function rawTilesToGrid(rawTiles, grid) {
10
+ return grid.fastCopyWith({
11
+ tiles: array(grid.width, grid.height, (x, y) => grid.getTile(x, y).withColor(rawTiles[y][x])),
12
+ });
13
+ }
14
+ function getNextTile(grid, rawTiles) {
15
+ for (let y = 0; y < grid.height; y++) {
16
+ for (let x = 0; x < grid.width; x++) {
17
+ const tile = grid.getTile(x, y);
18
+ if (!tile.exists || tile.fixed)
19
+ continue;
20
+ if (rawTiles[y][x] === Color.Gray)
21
+ return [{ x, y }, Color.Dark];
22
+ }
23
+ }
24
+ return undefined;
25
+ }
26
+ function backtrack(grid, rawTiles, submitSolution) {
27
+ // Find the best empty cell to guess
28
+ const target = getNextTile(grid, rawTiles);
29
+ // Found a solution
30
+ if (!target)
31
+ return !submitSolution(rawTiles);
32
+ const [pos, color] = target;
33
+ const positions = grid.connections.getConnectedTiles(pos);
34
+ for (let i = 0; i <= 1; i++) {
35
+ const tile = i === 0 ? color : color === Color.Dark ? Color.Light : Color.Dark;
36
+ positions.forEach(({ x, y }) => (rawTiles[y][x] = tile));
37
+ const newGrid = rawTilesToGrid(rawTiles, grid);
38
+ const isValid = validateGrid(newGrid, null);
39
+ if (isValid.final !== State.Error &&
40
+ backtrack(newGrid, rawTiles, submitSolution))
41
+ return true;
42
+ }
43
+ positions.forEach(({ x, y }) => (rawTiles[y][x] = Color.Gray));
44
+ return false;
45
+ }
46
+ function solveNormal(input, submitSolution) {
47
+ // Call backtrack
48
+ backtrack(input, gridToRawTiles(input), rawTiles => submitSolution(rawTiles ? rawTilesToGrid(rawTiles, input) : null));
49
+ }
50
+ function solveUnderclued(input) {
51
+ let grid = input;
52
+ const possibles = array(grid.width, grid.height, () => ({
53
+ dark: false,
54
+ light: false,
55
+ }));
56
+ function search(x, y, tile, color) {
57
+ const newGrid = grid.fastCopyWith({
58
+ tiles: grid.setTile(x, y, tile.withColor(color)),
59
+ });
60
+ // Solve
61
+ let solution;
62
+ solveNormal(newGrid, sol => {
63
+ solution = sol;
64
+ return false;
65
+ });
66
+ if (!solution)
67
+ return false;
68
+ // Update the new possible states
69
+ solution.forEach((solTile, solX, solY) => {
70
+ if (solTile.color === Color.Dark) {
71
+ possibles[solY][solX].dark = true;
72
+ }
73
+ else {
74
+ possibles[solY][solX].light = true;
75
+ }
76
+ });
77
+ return true;
78
+ }
79
+ for (let y = 0; y < grid.height; y++) {
80
+ for (let x = 0; x < grid.width; x++) {
81
+ const tile = grid.getTile(x, y);
82
+ if (!tile.exists || tile.color !== Color.Gray)
83
+ continue;
84
+ // We can skip this solve if it is proved to be solvable
85
+ const darkPossible = possibles[y][x].dark || search(x, y, tile, Color.Dark);
86
+ const lightPossible = possibles[y][x].light || search(x, y, tile, Color.Light);
87
+ // No solution
88
+ if (!darkPossible && !lightPossible)
89
+ return null;
90
+ if (darkPossible && !lightPossible)
91
+ grid = grid.fastCopyWith({
92
+ tiles: grid.setTile(x, y, tile.withColor(Color.Dark)),
93
+ });
94
+ if (!darkPossible && lightPossible)
95
+ grid = grid.fastCopyWith({
96
+ tiles: grid.setTile(x, y, tile.withColor(Color.Light)),
97
+ });
98
+ }
99
+ }
100
+ return grid;
101
+ }
102
+ function solve(grid, submitSolution) {
103
+ if (grid.findRule(rule => rule.id === undercluedInstance.id)) {
104
+ submitSolution(solveUnderclued(grid));
105
+ }
106
+ else {
107
+ solveNormal(grid, submitSolution);
108
+ }
109
+ }
110
+ onmessage = e => {
111
+ const grid = Serializer.parseGrid(e.data);
112
+ let count = 0;
113
+ solve(grid, solution => {
114
+ postMessage(solution ? Serializer.stringifyGrid(solution) : null);
115
+ count += 1;
116
+ return count < 2;
117
+ });
118
+ postMessage(null);
119
+ };
@@ -49,7 +49,7 @@ Object.defineProperty(CustomIconSymbol, "EXAMPLE_GRID", {
49
49
  enumerable: true,
50
50
  configurable: true,
51
51
  writable: true,
52
- value: Object.freeze(new GridData(5, 4))
52
+ value: Object.freeze(GridData.create(5, 4))
53
53
  });
54
54
  Object.defineProperty(CustomIconSymbol, "CONFIGS", {
55
55
  enumerable: true,
@@ -102,4 +102,4 @@ Object.defineProperty(CustomIconSymbol, "CONFIGS", {
102
102
  ])
103
103
  });
104
104
  export default CustomIconSymbol;
105
- export const instance = new CustomIconSymbol('A *custom* icon symbol', new GridData(5, 4), 0, 0, 'MdQuestionMark');
105
+ export const instance = new CustomIconSymbol('A *custom* icon symbol', GridData.create(5, 4), 0, 0, 'MdQuestionMark');
@@ -49,7 +49,7 @@ Object.defineProperty(CustomTextSymbol, "EXAMPLE_GRID", {
49
49
  enumerable: true,
50
50
  configurable: true,
51
51
  writable: true,
52
- value: Object.freeze(new GridData(5, 4))
52
+ value: Object.freeze(GridData.create(5, 4))
53
53
  });
54
54
  Object.defineProperty(CustomTextSymbol, "CONFIGS", {
55
55
  enumerable: true,
@@ -103,4 +103,4 @@ Object.defineProperty(CustomTextSymbol, "CONFIGS", {
103
103
  ])
104
104
  });
105
105
  export default CustomTextSymbol;
106
- export const instance = new CustomTextSymbol('A *custom* text symbol', new GridData(5, 4), 0, 0, 'X');
106
+ export const instance = new CustomTextSymbol('A *custom* text symbol', GridData.create(5, 4), 0, 0, 'X');
package/dist/index.d.ts CHANGED
@@ -64,8 +64,9 @@ import LotusBTModule from './data/solver/backtrack/symbols/lotus.js';
64
64
  import MinesweeperBTModule from './data/solver/backtrack/symbols/minesweeper.js';
65
65
  import MyopiaBTModule from './data/solver/backtrack/symbols/myopia.js';
66
66
  import ViewpointBTModule from './data/solver/backtrack/symbols/viewpoint.js';
67
+ import EventIteratingSolver from './data/solver/eventIteratingSolver.js';
67
68
  import Solver from './data/solver/solver.js';
68
- import UndercluedSolver from './data/solver/underclued/undercluedSolver.js';
69
+ import UniversalSolver from './data/solver/universal/universalSolver.js';
69
70
  import AreaNumberModule from './data/solver/z3/modules/areaNumberModule.js';
70
71
  import CellCountModule from './data/solver/z3/modules/cellCountModule.js';
71
72
  import ConnectAllModule from './data/solver/z3/modules/connectAllModule.js';
@@ -99,4 +100,4 @@ import ViewpointSymbol from './data/symbols/viewpointSymbol.js';
99
100
  import TileData from './data/tile.js';
100
101
  import TileConnections from './data/tileConnections.js';
101
102
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
102
- export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape, allSolvers, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, Solver, UndercluedSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, GalaxySymbol, HiddenSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
103
+ export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape, allSolvers, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, GalaxySymbol, HiddenSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
package/dist/index.js CHANGED
@@ -67,8 +67,9 @@ import LotusBTModule from './data/solver/backtrack/symbols/lotus.js';
67
67
  import MinesweeperBTModule from './data/solver/backtrack/symbols/minesweeper.js';
68
68
  import MyopiaBTModule from './data/solver/backtrack/symbols/myopia.js';
69
69
  import ViewpointBTModule from './data/solver/backtrack/symbols/viewpoint.js';
70
+ import EventIteratingSolver from './data/solver/eventIteratingSolver.js';
70
71
  import Solver from './data/solver/solver.js';
71
- import UndercluedSolver from './data/solver/underclued/undercluedSolver.js';
72
+ import UniversalSolver from './data/solver/universal/universalSolver.js';
72
73
  import AreaNumberModule from './data/solver/z3/modules/areaNumberModule.js';
73
74
  import CellCountModule from './data/solver/z3/modules/cellCountModule.js';
74
75
  import ConnectAllModule from './data/solver/z3/modules/connectAllModule.js';
@@ -102,4 +103,4 @@ import ViewpointSymbol from './data/symbols/viewpointSymbol.js';
102
103
  import TileData from './data/tile.js';
103
104
  import TileConnections from './data/tileConnections.js';
104
105
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
105
- export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape, allSolvers, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, Solver, UndercluedSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, GalaxySymbol, HiddenSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
106
+ export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape, allSolvers, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, GalaxySymbol, HiddenSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -1,8 +0,0 @@
1
- import GridData from '../../grid.js';
2
- import Solver from '../solver.js';
3
- export default class UndercluedSolver extends Solver {
4
- readonly id = "underclued";
5
- readonly description = "Solves every puzzle as if it were underclued. Supports all rules and symbols and is decently fast for small puzzles. Very slow for large puzzles.";
6
- solve(grid: GridData): AsyncGenerator<GridData | null>;
7
- isInstructionSupported(instructionId: string): boolean;
8
- }
@@ -1,55 +0,0 @@
1
- import { Color } from '../../primitives.js';
2
- import { instance as undercluedInstance } from '../../rules/undercluedRule.js';
3
- import { Serializer } from '../../serializer/allSerializers.js';
4
- import Solver from '../solver.js';
5
- export default class UndercluedSolver extends Solver {
6
- constructor() {
7
- super(...arguments);
8
- Object.defineProperty(this, "id", {
9
- enumerable: true,
10
- configurable: true,
11
- writable: true,
12
- value: 'underclued'
13
- });
14
- Object.defineProperty(this, "description", {
15
- enumerable: true,
16
- configurable: true,
17
- writable: true,
18
- value: 'Solves every puzzle as if it were underclued. Supports all rules and symbols and is decently fast for small puzzles. Very slow for large puzzles.'
19
- });
20
- }
21
- async *solve(grid) {
22
- const worker = new Worker(new URL('./undercluedWorker.js', import.meta.url), {
23
- type: 'module',
24
- });
25
- try {
26
- const solved = await new Promise(resolve => {
27
- worker.addEventListener('message', e => {
28
- const solution = Serializer.parseGrid(e.data);
29
- // console.timeEnd('Solve time');
30
- if (solution.resetTiles().equals(solution))
31
- resolve(null);
32
- else
33
- resolve(solution);
34
- });
35
- worker.postMessage(Serializer.stringifyGrid(grid));
36
- // console.time('Solve time');
37
- });
38
- yield solved;
39
- if (solved) {
40
- if (solved.getTileCount(true, undefined, Color.Gray) === 0) {
41
- yield null; // the grid is completely filled, which means the solution is unique
42
- }
43
- }
44
- }
45
- finally {
46
- worker.terminate();
47
- }
48
- }
49
- isInstructionSupported(instructionId) {
50
- if (super.isInstructionSupported(instructionId)) {
51
- return true;
52
- }
53
- return instructionId === undercluedInstance.id;
54
- }
55
- }
@@ -1,2 +0,0 @@
1
- declare const _default: null;
2
- export default _default;
@@ -1,135 +0,0 @@
1
- import { Color, State } from '../../primitives.js';
2
- import { Serializer } from '../../serializer/allSerializers.js';
3
- import validateGrid from '../../validate.js';
4
- function posToCoords(pos, width) {
5
- if (pos === undefined) {
6
- throw new Error('pos is undefined');
7
- }
8
- return [pos % width, Math.floor(pos / width)];
9
- }
10
- function coordsToPos(a, width) {
11
- return a[0] + a[1] * width;
12
- }
13
- function getValidGrid(grid, assumptions, canAssump) {
14
- while (true) {
15
- // Get assumption
16
- const newAssump = canAssump.findIndex((a, i) => a && !assumptions.includes(i));
17
- if (newAssump === -1) {
18
- return [grid, assumptions, true];
19
- }
20
- // Set assumption's color to dark
21
- const coords = posToCoords(newAssump, grid.width);
22
- grid = grid.setTile(coords[0], coords[1], tile => tile.withColor(Color.Dark));
23
- assumptions.push(newAssump);
24
- for (const a of grid.connections.getConnectedTiles({
25
- x: coords[0],
26
- y: coords[1],
27
- })) {
28
- canAssump[coordsToPos([a.x, a.y], grid.width)] =
29
- a.x === coords[0] && a.y === coords[1];
30
- }
31
- const state = validateGrid(grid, null);
32
- // If the grid is invalid, try to backtrack to a right assumption
33
- if (state.final === State.Error) {
34
- // eslint-disable-next-line @typescript-eslint/no-use-before-define
35
- [grid, assumptions] = tryToBacktrack(grid, assumptions);
36
- if (assumptions.length === 0) {
37
- return [grid, assumptions, false];
38
- }
39
- }
40
- }
41
- }
42
- function tryToBacktrack(grid, assumptions) {
43
- while (assumptions.length > 0) {
44
- const coords = posToCoords(assumptions[assumptions.length - 1], grid.width);
45
- if (grid.getTile(coords[0], coords[1]).color === Color.Light) {
46
- grid = grid.setTile(coords[0], coords[1], tile => tile.withColor(Color.Gray));
47
- assumptions.pop();
48
- }
49
- else {
50
- grid = grid.setTile(coords[0], coords[1], tile => tile.withColor(Color.Light));
51
- const state = validateGrid(grid, null);
52
- if (state.final === State.Error) {
53
- grid = grid.setTile(coords[0], coords[1], tile => tile.withColor(Color.Gray));
54
- assumptions.pop();
55
- }
56
- else {
57
- return [grid, assumptions];
58
- }
59
- }
60
- }
61
- return [grid, assumptions];
62
- }
63
- function computeSolution(initialGrid) {
64
- const canAssump = initialGrid.tiles
65
- .map(row => row.map(t => t.exists && !t.fixed))
66
- .flat();
67
- let lastValidGrid = [];
68
- let assumptions = [];
69
- let currentGrid = initialGrid.copyWith({});
70
- let anyNewGrid;
71
- while (assumptions.length > 0 || lastValidGrid.length === 0) {
72
- [currentGrid, assumptions, anyNewGrid] = getValidGrid(currentGrid, assumptions, canAssump);
73
- // console.log(
74
- // currentGrid.tiles
75
- // .map(row =>
76
- // row
77
- // .map(t => {
78
- // const color = t.color === Color.Light ? 'w' : 'b';
79
- // if (t.color === Color.Gray) return 'n';
80
- // if (!t.exists) return '.';
81
- // return t.fixed ? color.toUpperCase() : color;
82
- // })
83
- // .join('')
84
- // )
85
- // .join('\n')
86
- // );
87
- if (!anyNewGrid) {
88
- break;
89
- }
90
- const newLastValidGrid = currentGrid.tiles
91
- .map(row => row.map(t => t.color))
92
- .flat();
93
- if (lastValidGrid.length !== 0) {
94
- const diff = newLastValidGrid.map((color, i) => color === lastValidGrid[i]);
95
- diff.forEach((same, i) => {
96
- if (!same) {
97
- newLastValidGrid[i] = Color.Gray;
98
- }
99
- });
100
- }
101
- [currentGrid, assumptions] = tryToBacktrack(currentGrid, assumptions);
102
- lastValidGrid = newLastValidGrid;
103
- }
104
- // Create a new grid with lastValidGrid
105
- let solutionGrid = initialGrid.copyWith({});
106
- lastValidGrid.forEach((color, i) => {
107
- const coords = posToCoords(i, solutionGrid.width);
108
- solutionGrid = solutionGrid.setTile(coords[0], coords[1], tile => tile.withColor(color));
109
- });
110
- // console.log(
111
- // solutionGrid.tiles
112
- // .map(row =>
113
- // row
114
- // .map(t => {
115
- // const color = t.color === Color.Light ? 'w' : 'b';
116
- // if (t.color === Color.Gray) return 'n';
117
- // if (!t.exists) return '.';
118
- // return t.fixed ? color.toUpperCase() : color;
119
- // })
120
- // .join('')
121
- // )
122
- // .join('\n')
123
- // );
124
- return solutionGrid;
125
- }
126
- onmessage = e => {
127
- if (!e.data || typeof e.data !== 'string')
128
- return;
129
- const grid = Serializer.parseGrid(e.data);
130
- const solved = computeSolution(grid);
131
- postMessage(Serializer.stringifyGrid(solved));
132
- };
133
- // make typescript happy
134
- // eslint-disable-next-line import/no-anonymous-default-export
135
- export default null;