@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.
- package/assets/logic-core.global.d.ts +83 -22
- package/dist/data/grid.d.ts +44 -14
- package/dist/data/grid.js +43 -36
- package/dist/data/rules/banPatternRule.js +1 -1
- package/dist/data/rules/customRule.js +1 -1
- package/dist/data/rules/lyingSymbolRule.d.ts +1 -1
- package/dist/data/rules/lyingSymbolRule.js +38 -13
- package/dist/data/rules/musicGridRule.js +1 -1
- package/dist/data/rules/mysteryRule.js +1 -1
- package/dist/data/rules/regionAreaRule.js +3 -3
- package/dist/data/serializer/serializer_v0.js +1 -1
- package/dist/data/solver/allSolvers.js +2 -2
- package/dist/data/solver/backtrack/backtrackSolver.d.ts +3 -4
- package/dist/data/solver/backtrack/backtrackSolver.js +4 -30
- package/dist/data/solver/backtrack/backtrackWorker.d.ts +1 -2
- package/dist/data/solver/backtrack/backtrackWorker.js +12 -8
- package/dist/data/solver/eventIteratingSolver.d.ts +6 -0
- package/dist/data/solver/eventIteratingSolver.js +33 -0
- package/dist/data/solver/solver.d.ts +6 -1
- package/dist/data/solver/solver.js +2 -1
- package/dist/data/solver/universal/universalSolver.d.ts +7 -0
- package/dist/data/solver/universal/universalSolver.js +30 -0
- package/dist/data/solver/universal/universalWorker.d.ts +1 -0
- package/dist/data/solver/universal/universalWorker.js +119 -0
- package/dist/data/symbols/customIconSymbol.js +2 -2
- package/dist/data/symbols/customTextSymbol.js +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/package.json +1 -1
- package/dist/data/solver/underclued/undercluedSolver.d.ts +0 -8
- package/dist/data/solver/underclued/undercluedSolver.js +0 -55
- package/dist/data/solver/underclued/undercluedWorker.d.ts +0 -2
- 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
|
-
|
|
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
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
264
|
+
grid = grid.fastCopyWith({
|
|
265
|
+
tiles: grid.setTile(x, y, tile.withColor(Color.Dark)),
|
|
266
|
+
});
|
|
262
267
|
if (!darkPossible && lightPossible)
|
|
263
|
-
grid = grid.
|
|
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 =>
|
|
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(
|
|
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',
|
|
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(
|
|
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',
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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,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,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;
|