@logic-pad/core 0.1.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/LICENSE +661 -0
- package/README.md +19 -0
- package/assets/logic-core.global.d.ts +5865 -0
- package/assets/z3-built.js +14723 -0
- package/assets/z3-built.wasm +0 -0
- package/assets/z3-built.worker.js +206 -0
- package/dist/data/config.d.ts +101 -0
- package/dist/data/config.js +55 -0
- package/dist/data/configurable.d.ts +12 -0
- package/dist/data/configurable.js +26 -0
- package/dist/data/dataHelper.d.ts +77 -0
- package/dist/data/dataHelper.js +190 -0
- package/dist/data/events/eventHelper.d.ts +1 -0
- package/dist/data/events/eventHelper.js +6 -0
- package/dist/data/events/onFinalValidation.d.ts +14 -0
- package/dist/data/events/onFinalValidation.js +4 -0
- package/dist/data/events/onGridChange.d.ts +6 -0
- package/dist/data/events/onGridChange.js +4 -0
- package/dist/data/events/onGridResize.d.ts +9 -0
- package/dist/data/events/onGridResize.js +4 -0
- package/dist/data/events/onSetGrid.d.ts +6 -0
- package/dist/data/events/onSetGrid.js +4 -0
- package/dist/data/events/onSymbolValidation.d.ts +18 -0
- package/dist/data/events/onSymbolValidation.js +4 -0
- package/dist/data/grid.d.ts +362 -0
- package/dist/data/grid.js +886 -0
- package/dist/data/gridConnections.d.ts +38 -0
- package/dist/data/gridConnections.js +328 -0
- package/dist/data/instruction.d.ts +19 -0
- package/dist/data/instruction.js +23 -0
- package/dist/data/primitives.d.ts +85 -0
- package/dist/data/primitives.js +90 -0
- package/dist/data/puzzle.d.ts +86 -0
- package/dist/data/puzzle.js +22 -0
- package/dist/data/rules/banPatternRule.d.ts +29 -0
- package/dist/data/rules/banPatternRule.js +133 -0
- package/dist/data/rules/cellCountRule.d.ts +32 -0
- package/dist/data/rules/cellCountRule.js +166 -0
- package/dist/data/rules/completePatternRule.d.ts +22 -0
- package/dist/data/rules/completePatternRule.js +53 -0
- package/dist/data/rules/connectAllRule.d.ts +28 -0
- package/dist/data/rules/connectAllRule.js +113 -0
- package/dist/data/rules/customRule.d.ts +32 -0
- package/dist/data/rules/customRule.js +92 -0
- package/dist/data/rules/foresightRule.d.ts +30 -0
- package/dist/data/rules/foresightRule.js +107 -0
- package/dist/data/rules/index.d.ts +3 -0
- package/dist/data/rules/index.js +10 -0
- package/dist/data/rules/musicControlLine.d.ts +64 -0
- package/dist/data/rules/musicControlLine.js +178 -0
- package/dist/data/rules/musicGridRule.d.ts +46 -0
- package/dist/data/rules/musicGridRule.js +211 -0
- package/dist/data/rules/mysteryRule.d.ts +37 -0
- package/dist/data/rules/mysteryRule.js +164 -0
- package/dist/data/rules/offByXRule.d.ts +30 -0
- package/dist/data/rules/offByXRule.js +134 -0
- package/dist/data/rules/regionAreaRule.d.ts +33 -0
- package/dist/data/rules/regionAreaRule.js +182 -0
- package/dist/data/rules/regionShapeRule.d.ts +22 -0
- package/dist/data/rules/regionShapeRule.js +58 -0
- package/dist/data/rules/rule.d.ts +18 -0
- package/dist/data/rules/rule.js +19 -0
- package/dist/data/rules/rules.gen.d.ts +14 -0
- package/dist/data/rules/rules.gen.js +18 -0
- package/dist/data/rules/sameShapeRule.d.ts +27 -0
- package/dist/data/rules/sameShapeRule.js +88 -0
- package/dist/data/rules/symbolsPerRegionRule.d.ts +37 -0
- package/dist/data/rules/symbolsPerRegionRule.js +211 -0
- package/dist/data/rules/undercluedRule.d.ts +22 -0
- package/dist/data/rules/undercluedRule.js +60 -0
- package/dist/data/rules/uniqueShapeRule.d.ts +27 -0
- package/dist/data/rules/uniqueShapeRule.js +85 -0
- package/dist/data/serializer/allSerializers.d.ts +30 -0
- package/dist/data/serializer/allSerializers.js +64 -0
- package/dist/data/serializer/compressor/allCompressors.d.ts +14 -0
- package/dist/data/serializer/compressor/allCompressors.js +43 -0
- package/dist/data/serializer/compressor/compressorBase.d.ts +16 -0
- package/dist/data/serializer/compressor/compressorBase.js +2 -0
- package/dist/data/serializer/compressor/deflateCompressor.d.ts +7 -0
- package/dist/data/serializer/compressor/deflateCompressor.js +17 -0
- package/dist/data/serializer/compressor/gzipCompressor.d.ts +5 -0
- package/dist/data/serializer/compressor/gzipCompressor.js +9 -0
- package/dist/data/serializer/compressor/streamCompressor.d.ts +6 -0
- package/dist/data/serializer/compressor/streamCompressor.js +36 -0
- package/dist/data/serializer/serializerBase.d.ts +27 -0
- package/dist/data/serializer/serializerBase.js +2 -0
- package/dist/data/serializer/serializer_v0.d.ts +36 -0
- package/dist/data/serializer/serializer_v0.js +426 -0
- package/dist/data/shapes.d.ts +17 -0
- package/dist/data/shapes.js +117 -0
- package/dist/data/solver/allSolvers.d.ts +3 -0
- package/dist/data/solver/allSolvers.js +11 -0
- package/dist/data/solver/backtrack/backtrackSolver.d.ts +9 -0
- package/dist/data/solver/backtrack/backtrackSolver.js +92 -0
- package/dist/data/solver/backtrack/backtrackWorker.d.ts +2 -0
- package/dist/data/solver/backtrack/backtrackWorker.js +295 -0
- package/dist/data/solver/backtrack/data.d.ts +46 -0
- package/dist/data/solver/backtrack/data.js +140 -0
- package/dist/data/solver/backtrack/rules/banPattern.d.ts +9 -0
- package/dist/data/solver/backtrack/rules/banPattern.js +66 -0
- package/dist/data/solver/backtrack/rules/cellCount.d.ts +7 -0
- package/dist/data/solver/backtrack/rules/cellCount.js +30 -0
- package/dist/data/solver/backtrack/rules/connectAll.d.ts +7 -0
- package/dist/data/solver/backtrack/rules/connectAll.js +49 -0
- package/dist/data/solver/backtrack/rules/regionArea.d.ts +8 -0
- package/dist/data/solver/backtrack/rules/regionArea.js +76 -0
- package/dist/data/solver/backtrack/rules/regionShape.d.ts +8 -0
- package/dist/data/solver/backtrack/rules/regionShape.js +62 -0
- package/dist/data/solver/backtrack/rules/sameShape.d.ts +8 -0
- package/dist/data/solver/backtrack/rules/sameShape.js +19 -0
- package/dist/data/solver/backtrack/rules/symbolsPerRegion.d.ts +10 -0
- package/dist/data/solver/backtrack/rules/symbolsPerRegion.js +92 -0
- package/dist/data/solver/backtrack/rules/uniqueShape.d.ts +8 -0
- package/dist/data/solver/backtrack/rules/uniqueShape.js +19 -0
- package/dist/data/solver/backtrack/symbols/areaNumber.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/areaNumber.js +77 -0
- package/dist/data/solver/backtrack/symbols/dart.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/dart.js +58 -0
- package/dist/data/solver/backtrack/symbols/directionLinker.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/directionLinker.js +50 -0
- package/dist/data/solver/backtrack/symbols/galaxy.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/galaxy.js +19 -0
- package/dist/data/solver/backtrack/symbols/letter.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/letter.js +100 -0
- package/dist/data/solver/backtrack/symbols/lotus.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/lotus.js +36 -0
- package/dist/data/solver/backtrack/symbols/minesweeper.d.ts +9 -0
- package/dist/data/solver/backtrack/symbols/minesweeper.js +55 -0
- package/dist/data/solver/backtrack/symbols/myopia.d.ts +7 -0
- package/dist/data/solver/backtrack/symbols/myopia.js +79 -0
- package/dist/data/solver/backtrack/symbols/viewpoint.d.ts +7 -0
- package/dist/data/solver/backtrack/symbols/viewpoint.js +56 -0
- package/dist/data/solver/solver.d.ts +61 -0
- package/dist/data/solver/solver.js +55 -0
- package/dist/data/solver/underclued/undercluedSolver.d.ts +8 -0
- package/dist/data/solver/underclued/undercluedSolver.js +55 -0
- package/dist/data/solver/underclued/undercluedWorker.d.ts +2 -0
- package/dist/data/solver/underclued/undercluedWorker.js +131 -0
- package/dist/data/solver/z3/modules/areaNumberModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/areaNumberModule.js +35 -0
- package/dist/data/solver/z3/modules/cellCountModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/cellCountModule.js +59 -0
- package/dist/data/solver/z3/modules/connectAllModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/connectAllModule.js +32 -0
- package/dist/data/solver/z3/modules/dartModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/dartModule.js +69 -0
- package/dist/data/solver/z3/modules/index.d.ts +3 -0
- package/dist/data/solver/z3/modules/index.js +10 -0
- package/dist/data/solver/z3/modules/letterModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/letterModule.js +41 -0
- package/dist/data/solver/z3/modules/modules.gen.d.ts +8 -0
- package/dist/data/solver/z3/modules/modules.gen.js +12 -0
- package/dist/data/solver/z3/modules/myopiaModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/myopiaModule.js +64 -0
- package/dist/data/solver/z3/modules/regionAreaModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/regionAreaModule.js +48 -0
- package/dist/data/solver/z3/modules/viewpointModule.d.ts +9 -0
- package/dist/data/solver/z3/modules/viewpointModule.js +37 -0
- package/dist/data/solver/z3/modules/z3Module.d.ts +7 -0
- package/dist/data/solver/z3/modules/z3Module.js +3 -0
- package/dist/data/solver/z3/utils.d.ts +2 -0
- package/dist/data/solver/z3/utils.js +26 -0
- package/dist/data/solver/z3/z3Solver.d.ts +10 -0
- package/dist/data/solver/z3/z3Solver.js +134 -0
- package/dist/data/solver/z3/z3SolverContext.d.ts +808 -0
- package/dist/data/solver/z3/z3SolverContext.js +49 -0
- package/dist/data/symbols/areaNumberSymbol.d.ts +30 -0
- package/dist/data/symbols/areaNumberSymbol.js +88 -0
- package/dist/data/symbols/customIconSymbol.d.ts +35 -0
- package/dist/data/symbols/customIconSymbol.js +105 -0
- package/dist/data/symbols/customSymbol.d.ts +23 -0
- package/dist/data/symbols/customSymbol.js +48 -0
- package/dist/data/symbols/customTextSymbol.d.ts +33 -0
- package/dist/data/symbols/customTextSymbol.js +106 -0
- package/dist/data/symbols/dartSymbol.d.ts +35 -0
- package/dist/data/symbols/dartSymbol.js +110 -0
- package/dist/data/symbols/directionLinkerSymbol.d.ts +36 -0
- package/dist/data/symbols/directionLinkerSymbol.js +259 -0
- package/dist/data/symbols/galaxySymbol.d.ts +26 -0
- package/dist/data/symbols/galaxySymbol.js +74 -0
- package/dist/data/symbols/index.d.ts +3 -0
- package/dist/data/symbols/index.js +10 -0
- package/dist/data/symbols/letterSymbol.d.ts +31 -0
- package/dist/data/symbols/letterSymbol.js +137 -0
- package/dist/data/symbols/lotusSymbol.d.ts +29 -0
- package/dist/data/symbols/lotusSymbol.js +132 -0
- package/dist/data/symbols/minesweeperSymbol.d.ts +31 -0
- package/dist/data/symbols/minesweeperSymbol.js +100 -0
- package/dist/data/symbols/multiEntrySymbol.d.ts +11 -0
- package/dist/data/symbols/multiEntrySymbol.js +14 -0
- package/dist/data/symbols/myopiaSymbol.d.ts +34 -0
- package/dist/data/symbols/myopiaSymbol.js +187 -0
- package/dist/data/symbols/numberSymbol.d.ts +19 -0
- package/dist/data/symbols/numberSymbol.js +41 -0
- package/dist/data/symbols/symbol.d.ts +16 -0
- package/dist/data/symbols/symbol.js +51 -0
- package/dist/data/symbols/symbols.gen.d.ts +10 -0
- package/dist/data/symbols/symbols.gen.js +14 -0
- package/dist/data/symbols/viewpointSymbol.d.ts +31 -0
- package/dist/data/symbols/viewpointSymbol.js +106 -0
- package/dist/data/tile.d.ts +26 -0
- package/dist/data/tile.js +68 -0
- package/dist/data/tileConnections.d.ts +25 -0
- package/dist/data/tileConnections.js +74 -0
- package/dist/data/validate.d.ts +5 -0
- package/dist/data/validate.js +131 -0
- package/dist/index.d.ts +96 -0
- package/dist/index.js +100 -0
- package/dist/polyfill/streamPolyfill.d.ts +2 -0
- package/dist/polyfill/streamPolyfill.js +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import EventIterator from 'event-iterator';
|
|
2
|
+
import { instance as banPatternInstance } from '../../rules/banPatternRule';
|
|
3
|
+
import { instance as cellCountInstance } from '../../rules/cellCountRule';
|
|
4
|
+
import { instance as regionAreaInstance } from '../../rules/regionAreaRule';
|
|
5
|
+
import { instance as sameShapeInstance } from '../../rules/sameShapeRule';
|
|
6
|
+
import { instance as symbolsPerRegionInstance } from '../../rules/symbolsPerRegionRule';
|
|
7
|
+
import { instance as undercluedInstance } from '../../rules/undercluedRule';
|
|
8
|
+
import { instance as uniqueShapeInstance } from '../../rules/uniqueShapeRule';
|
|
9
|
+
import { Serializer } from '../../serializer/allSerializers';
|
|
10
|
+
import { instance as areaNumberInstance } from '../../symbols/areaNumberSymbol';
|
|
11
|
+
import { instance as dartInstance } from '../../symbols/dartSymbol';
|
|
12
|
+
import { instance as galaxyInstance } from '../../symbols/galaxySymbol';
|
|
13
|
+
import { instance as letterInstance } from '../../symbols/letterSymbol';
|
|
14
|
+
import { instance as lotusInstance } from '../../symbols/lotusSymbol';
|
|
15
|
+
import { instance as minesweeperInstance } from '../../symbols/minesweeperSymbol';
|
|
16
|
+
import { instance as myopiaInstance } from '../../symbols/myopiaSymbol';
|
|
17
|
+
import { instance as viewpointInstance } from '../../symbols/viewpointSymbol';
|
|
18
|
+
import Solver from '../solver';
|
|
19
|
+
import { instance as connectAllInstance } from '../z3/modules/connectAllModule';
|
|
20
|
+
class BacktrackSolver extends Solver {
|
|
21
|
+
constructor() {
|
|
22
|
+
super(...arguments);
|
|
23
|
+
Object.defineProperty(this, "id", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: 'backtrack'
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "description", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: 'Solves puzzles using backtracking with optimizations (blazingly fast). Support most rules and symbols (including underclued).'
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
async *solve(grid) {
|
|
37
|
+
const worker = new Worker(new URL('./backtrackWorker.js', import.meta.url), {
|
|
38
|
+
type: 'module',
|
|
39
|
+
});
|
|
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(new Error(e.message));
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
for await (const solution of iterator) {
|
|
57
|
+
yield solution;
|
|
58
|
+
}
|
|
59
|
+
yield null;
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
worker.terminate();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
isInstructionSupported(instructionId) {
|
|
66
|
+
return BacktrackSolver.supportedInstrs.includes(instructionId);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
Object.defineProperty(BacktrackSolver, "supportedInstrs", {
|
|
70
|
+
enumerable: true,
|
|
71
|
+
configurable: true,
|
|
72
|
+
writable: true,
|
|
73
|
+
value: [
|
|
74
|
+
areaNumberInstance.id,
|
|
75
|
+
viewpointInstance.id,
|
|
76
|
+
dartInstance.id,
|
|
77
|
+
galaxyInstance.id,
|
|
78
|
+
lotusInstance.id,
|
|
79
|
+
myopiaInstance.id,
|
|
80
|
+
minesweeperInstance.id,
|
|
81
|
+
letterInstance.id,
|
|
82
|
+
undercluedInstance.id,
|
|
83
|
+
connectAllInstance.id,
|
|
84
|
+
banPatternInstance.id,
|
|
85
|
+
regionAreaInstance.id,
|
|
86
|
+
symbolsPerRegionInstance.id,
|
|
87
|
+
cellCountInstance.id,
|
|
88
|
+
sameShapeInstance.id,
|
|
89
|
+
uniqueShapeInstance.id,
|
|
90
|
+
]
|
|
91
|
+
});
|
|
92
|
+
export default BacktrackSolver;
|
|
@@ -0,0 +1,295 @@
|
|
|
1
|
+
import { array } from '../../dataHelper';
|
|
2
|
+
import { Color } from '../../primitives';
|
|
3
|
+
import { instance as banPatternInstance, } from '../../rules/banPatternRule';
|
|
4
|
+
import { instance as cellCountInstance, } from '../../rules/cellCountRule';
|
|
5
|
+
import { instance as regionAreaInstance, } from '../../rules/regionAreaRule';
|
|
6
|
+
import { instance as sameShapeInstance, } from '../../rules/sameShapeRule';
|
|
7
|
+
import { instance as symbolsPerRegionInstance, } from '../../rules/symbolsPerRegionRule';
|
|
8
|
+
import { instance as undercluedInstance } from '../../rules/undercluedRule';
|
|
9
|
+
import { instance as uniqueShapeInstance, } from '../../rules/uniqueShapeRule';
|
|
10
|
+
import { Serializer } from '../../serializer/allSerializers';
|
|
11
|
+
import { instance as areaNumberInstance, } from '../../symbols/areaNumberSymbol';
|
|
12
|
+
import { instance as dartInstance } from '../../symbols/dartSymbol';
|
|
13
|
+
import { instance as galaxyInstance, } from '../../symbols/galaxySymbol';
|
|
14
|
+
import { instance as letterInstance, } from '../../symbols/letterSymbol';
|
|
15
|
+
import { instance as lotusInstance, } from '../../symbols/lotusSymbol';
|
|
16
|
+
import { instance as minesweeperInstance, } from '../../symbols/minesweeperSymbol';
|
|
17
|
+
import { instance as myopiaInstance, } from '../../symbols/myopiaSymbol';
|
|
18
|
+
import { instance as viewpointInstance, } from '../../symbols/viewpointSymbol';
|
|
19
|
+
import { instance as connectAllInstance } from '../z3/modules/connectAllModule';
|
|
20
|
+
import { BTGridData, BTTile } from './data';
|
|
21
|
+
import BanPatternBTModule from './rules/banPattern';
|
|
22
|
+
import CellCountBTModule from './rules/cellCount';
|
|
23
|
+
import ConnectAllBTModule from './rules/connectAll';
|
|
24
|
+
import RegionAreaBTModule from './rules/regionArea';
|
|
25
|
+
import SameShapeBTModule from './rules/sameShape';
|
|
26
|
+
import SymbolsPerRegionBTModule from './rules/symbolsPerRegion';
|
|
27
|
+
import UniqueShapeBTModule from './rules/uniqueShape';
|
|
28
|
+
import AreaNumberBTModule from './symbols/areaNumber';
|
|
29
|
+
import DartBTModule from './symbols/dart';
|
|
30
|
+
import GalaxyBTModule from './symbols/galaxy';
|
|
31
|
+
import LetterBTModule from './symbols/letter';
|
|
32
|
+
import LotusBTModule from './symbols/lotus';
|
|
33
|
+
import MinesweeperBTModule from './symbols/minesweeper';
|
|
34
|
+
import MyopiaBTModule from './symbols/myopia';
|
|
35
|
+
import ViewpointBTModule from './symbols/viewpoint';
|
|
36
|
+
function translateToBTGridData(grid) {
|
|
37
|
+
const tiles = array(grid.width, grid.height, (x, y) => {
|
|
38
|
+
const tile = grid.getTile(x, y);
|
|
39
|
+
if (!tile.exists)
|
|
40
|
+
return BTTile.NonExist;
|
|
41
|
+
else if (tile.color === Color.Dark)
|
|
42
|
+
return BTTile.Dark;
|
|
43
|
+
else if (tile.color === Color.Light)
|
|
44
|
+
return BTTile.Light;
|
|
45
|
+
else
|
|
46
|
+
return BTTile.Empty;
|
|
47
|
+
});
|
|
48
|
+
const connections = array(grid.width, grid.height, (x, y) => grid.connections.getConnectedTiles({ x, y }));
|
|
49
|
+
const modules = [];
|
|
50
|
+
for (const [id, symbolList] of grid.symbols) {
|
|
51
|
+
for (const symbol of symbolList) {
|
|
52
|
+
let module;
|
|
53
|
+
if (id === areaNumberInstance.id) {
|
|
54
|
+
module = new AreaNumberBTModule(symbol);
|
|
55
|
+
}
|
|
56
|
+
else if (id === dartInstance.id) {
|
|
57
|
+
module = new DartBTModule(symbol);
|
|
58
|
+
}
|
|
59
|
+
else if (id === viewpointInstance.id) {
|
|
60
|
+
module = new ViewpointBTModule(symbol);
|
|
61
|
+
}
|
|
62
|
+
else if (id === galaxyInstance.id) {
|
|
63
|
+
module = new GalaxyBTModule(symbol);
|
|
64
|
+
}
|
|
65
|
+
else if (id === lotusInstance.id) {
|
|
66
|
+
module = new LotusBTModule(symbol);
|
|
67
|
+
}
|
|
68
|
+
else if (id === myopiaInstance.id) {
|
|
69
|
+
module = new MyopiaBTModule(symbol);
|
|
70
|
+
}
|
|
71
|
+
else if (id === minesweeperInstance.id) {
|
|
72
|
+
module = new MinesweeperBTModule(symbol);
|
|
73
|
+
}
|
|
74
|
+
else if (id === letterInstance.id) {
|
|
75
|
+
continue;
|
|
76
|
+
}
|
|
77
|
+
if (!module)
|
|
78
|
+
throw new Error('Symbol not supported.');
|
|
79
|
+
modules.push(module);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
const letterSymbols = grid.symbols.get(letterInstance.id);
|
|
83
|
+
if (letterSymbols) {
|
|
84
|
+
modules.push(new LetterBTModule(letterSymbols, grid.width, grid.height));
|
|
85
|
+
}
|
|
86
|
+
for (const rule of grid.rules) {
|
|
87
|
+
if (!rule.necessaryForCompletion)
|
|
88
|
+
continue;
|
|
89
|
+
let module;
|
|
90
|
+
if (rule.id === connectAllInstance.id) {
|
|
91
|
+
module = new ConnectAllBTModule(rule);
|
|
92
|
+
}
|
|
93
|
+
else if (rule.id === regionAreaInstance.id) {
|
|
94
|
+
module = new RegionAreaBTModule(rule);
|
|
95
|
+
}
|
|
96
|
+
else if (rule.id === banPatternInstance.id) {
|
|
97
|
+
module = new BanPatternBTModule(rule);
|
|
98
|
+
}
|
|
99
|
+
else if (rule.id === symbolsPerRegionInstance.id) {
|
|
100
|
+
const allSymbols = [];
|
|
101
|
+
grid.symbols.forEach(symbols => allSymbols.push(...symbols));
|
|
102
|
+
module = new SymbolsPerRegionBTModule(rule, grid.width, grid.height, allSymbols);
|
|
103
|
+
}
|
|
104
|
+
else if (rule.id === cellCountInstance.id) {
|
|
105
|
+
module = new CellCountBTModule(rule);
|
|
106
|
+
}
|
|
107
|
+
else if (rule.id === sameShapeInstance.id) {
|
|
108
|
+
module = new SameShapeBTModule(rule);
|
|
109
|
+
}
|
|
110
|
+
else if (rule.id === uniqueShapeInstance.id) {
|
|
111
|
+
module = new UniqueShapeBTModule(rule);
|
|
112
|
+
}
|
|
113
|
+
else if (rule.id === undercluedInstance.id) {
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (!module)
|
|
117
|
+
throw new Error('Rule not supported.');
|
|
118
|
+
modules.push(module);
|
|
119
|
+
}
|
|
120
|
+
return new BTGridData(tiles, connections, modules, grid.width, grid.height);
|
|
121
|
+
}
|
|
122
|
+
function translateBackGridData(grid, btGrid) {
|
|
123
|
+
const tiles = array(grid.width, grid.height, (x, y) => {
|
|
124
|
+
const origTile = grid.getTile(x, y);
|
|
125
|
+
if (!origTile.exists || origTile.fixed || origTile.color !== Color.Gray)
|
|
126
|
+
return origTile;
|
|
127
|
+
else
|
|
128
|
+
return origTile.withColor(btGrid.getTile(x, y) === BTTile.Dark ? Color.Dark : Color.Light);
|
|
129
|
+
});
|
|
130
|
+
return grid.withTiles(tiles);
|
|
131
|
+
}
|
|
132
|
+
function isValid(grid, places, checkable, ratings) {
|
|
133
|
+
const newCheckable = [...checkable];
|
|
134
|
+
const newRatings = [...ratings];
|
|
135
|
+
for (let i = 0; i < grid.modules.length; i++) {
|
|
136
|
+
const module = grid.modules[i];
|
|
137
|
+
// Check if skippable
|
|
138
|
+
if (checkable[i] && !places.some(pos => checkable[i].get(pos.x, pos.y)))
|
|
139
|
+
continue;
|
|
140
|
+
const result = module.checkLocal(grid, places);
|
|
141
|
+
if (!result)
|
|
142
|
+
return false;
|
|
143
|
+
// If returns true, it means do not change checkable and ratings
|
|
144
|
+
if (result === true)
|
|
145
|
+
continue;
|
|
146
|
+
newCheckable[i] = result.tilesNeedCheck;
|
|
147
|
+
newRatings[i] = result.ratings;
|
|
148
|
+
}
|
|
149
|
+
return [newCheckable, newRatings];
|
|
150
|
+
}
|
|
151
|
+
// This function chooses the next empty tile to search.
|
|
152
|
+
function getNextTile(grid, ratings) {
|
|
153
|
+
const scores = [];
|
|
154
|
+
// TODO: Sum up all the scores of connected tiles without overcounting
|
|
155
|
+
for (let y = 0; y < grid.height; y++) {
|
|
156
|
+
scores[y] = [];
|
|
157
|
+
for (let x = 0; x < grid.width; x++) {
|
|
158
|
+
scores[y][x] = 0;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
for (const rating of ratings) {
|
|
162
|
+
if (!rating)
|
|
163
|
+
continue;
|
|
164
|
+
for (const score of rating) {
|
|
165
|
+
scores[score.pos.y][score.pos.x] += score.score;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let highest = 0;
|
|
169
|
+
let pos = null;
|
|
170
|
+
let fallback = null;
|
|
171
|
+
for (let y = 0; y < grid.height; y++) {
|
|
172
|
+
for (let x = 0; x < grid.width; x++) {
|
|
173
|
+
if (grid.getTile(x, y) !== BTTile.Empty)
|
|
174
|
+
continue;
|
|
175
|
+
if (scores[y][x] > highest) {
|
|
176
|
+
highest = scores[y][x];
|
|
177
|
+
pos = { x, y };
|
|
178
|
+
}
|
|
179
|
+
if (!fallback)
|
|
180
|
+
fallback = { x, y };
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return pos ?? fallback;
|
|
184
|
+
}
|
|
185
|
+
function backtrack(grid, checkable, ratings, solutionFn) {
|
|
186
|
+
// Find the best empty cell to guess
|
|
187
|
+
const pos = getNextTile(grid, ratings);
|
|
188
|
+
// Found a solution
|
|
189
|
+
if (!pos)
|
|
190
|
+
return !solutionFn(grid.clone());
|
|
191
|
+
for (let i = 0; i <= 1; i++) {
|
|
192
|
+
// TODO: Use a better method to determine the order
|
|
193
|
+
const tile = i === 0 ? BTTile.Light : BTTile.Dark;
|
|
194
|
+
grid.setTileWithConnection(pos.x, pos.y, tile);
|
|
195
|
+
const places = grid.connections[pos.y][pos.x];
|
|
196
|
+
const result = isValid(grid, places, checkable, ratings);
|
|
197
|
+
if (result && backtrack(grid, result[0], result[1], solutionFn))
|
|
198
|
+
return true;
|
|
199
|
+
}
|
|
200
|
+
// If both fail, returns to initial state
|
|
201
|
+
grid.setTileWithConnection(pos.x, pos.y, BTTile.Empty);
|
|
202
|
+
return false;
|
|
203
|
+
}
|
|
204
|
+
function solveNormal(input, solutionFn) {
|
|
205
|
+
// Translate to BT data types
|
|
206
|
+
const grid = translateToBTGridData(input);
|
|
207
|
+
const checkable = [];
|
|
208
|
+
const ratings = [];
|
|
209
|
+
for (const module of grid.modules) {
|
|
210
|
+
const res = module.checkGlobal(grid);
|
|
211
|
+
if (!res)
|
|
212
|
+
return [];
|
|
213
|
+
checkable.push(res.tilesNeedCheck);
|
|
214
|
+
ratings.push(res.ratings);
|
|
215
|
+
}
|
|
216
|
+
// Call backtrack
|
|
217
|
+
backtrack(grid, checkable, ratings, sol => solutionFn(translateBackGridData(input, sol)));
|
|
218
|
+
}
|
|
219
|
+
function solveUnderclued(input) {
|
|
220
|
+
let grid = input;
|
|
221
|
+
let count = 0;
|
|
222
|
+
const possibles = array(grid.width, grid.height, () => ({
|
|
223
|
+
dark: false,
|
|
224
|
+
light: false,
|
|
225
|
+
}));
|
|
226
|
+
function search(x, y, tile, color) {
|
|
227
|
+
count++;
|
|
228
|
+
// console.log(`Trying (${x}, ${y}) with ${color}`);
|
|
229
|
+
const newGrid = grid.setTile(x, y, tile.withColor(color));
|
|
230
|
+
// Solve
|
|
231
|
+
let solution;
|
|
232
|
+
solveNormal(newGrid, sol => {
|
|
233
|
+
solution = sol;
|
|
234
|
+
return false;
|
|
235
|
+
});
|
|
236
|
+
if (!solution)
|
|
237
|
+
return false;
|
|
238
|
+
// Update the new possible states
|
|
239
|
+
solution.forEach((solTile, solX, solY) => {
|
|
240
|
+
if (solTile.color === Color.Dark) {
|
|
241
|
+
possibles[solY][solX].dark = true;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
possibles[solY][solX].light = true;
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
for (let y = 0; y < grid.height; y++) {
|
|
250
|
+
for (let x = 0; x < grid.width; x++) {
|
|
251
|
+
const tile = grid.getTile(x, y);
|
|
252
|
+
if (!tile.exists || tile.color !== Color.Gray)
|
|
253
|
+
continue;
|
|
254
|
+
// We can skip this solve if it is proved to be solvable
|
|
255
|
+
const darkPossible = possibles[y][x].dark || search(x, y, tile, Color.Dark);
|
|
256
|
+
const lightPossible = possibles[y][x].light || search(x, y, tile, Color.Light);
|
|
257
|
+
// No solution
|
|
258
|
+
if (!darkPossible && !lightPossible)
|
|
259
|
+
return null;
|
|
260
|
+
if (darkPossible && !lightPossible)
|
|
261
|
+
grid = grid.setTile(x, y, tile.withColor(Color.Dark));
|
|
262
|
+
if (!darkPossible && lightPossible)
|
|
263
|
+
grid = grid.setTile(x, y, tile.withColor(Color.Light));
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
console.log(`Solve count: ${count}`);
|
|
267
|
+
return grid;
|
|
268
|
+
}
|
|
269
|
+
function solve(grid, solutionFn) {
|
|
270
|
+
if (grid.findRule(rule => rule.id === undercluedInstance.id)) {
|
|
271
|
+
const res = solveUnderclued(grid);
|
|
272
|
+
if (res)
|
|
273
|
+
solutionFn(res);
|
|
274
|
+
}
|
|
275
|
+
else {
|
|
276
|
+
solveNormal(grid, solutionFn);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
onmessage = e => {
|
|
280
|
+
const grid = Serializer.parseGrid(e.data);
|
|
281
|
+
console.time('Solve time');
|
|
282
|
+
let count = 0;
|
|
283
|
+
solve(grid, solution => {
|
|
284
|
+
if (count === 0)
|
|
285
|
+
console.timeLog('Solve time', 'First solution');
|
|
286
|
+
postMessage(Serializer.stringifyGrid(solution));
|
|
287
|
+
count += 1;
|
|
288
|
+
return count < 2;
|
|
289
|
+
});
|
|
290
|
+
console.timeEnd('Solve time');
|
|
291
|
+
postMessage(null);
|
|
292
|
+
};
|
|
293
|
+
// make typescript happy
|
|
294
|
+
// eslint-disable-next-line import/no-anonymous-default-export
|
|
295
|
+
export default null;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Color, Position } from '../../primitives';
|
|
2
|
+
export declare enum BTTile {
|
|
3
|
+
Empty = 0,
|
|
4
|
+
Dark = 1,
|
|
5
|
+
Light = 2,
|
|
6
|
+
NonExist = 3
|
|
7
|
+
}
|
|
8
|
+
export type BTColor = BTTile.Dark | BTTile.Light;
|
|
9
|
+
export declare class BTGridData {
|
|
10
|
+
readonly tiles: BTTile[][];
|
|
11
|
+
readonly connections: Position[][][];
|
|
12
|
+
readonly modules: BTModule[];
|
|
13
|
+
readonly width: number;
|
|
14
|
+
readonly height: number;
|
|
15
|
+
constructor(tiles: BTTile[][], connections: Position[][][], modules: BTModule[], width: number, height: number);
|
|
16
|
+
getTile(x: number, y: number): BTTile;
|
|
17
|
+
setTileWithConnection(x: number, y: number, tile: BTTile): void;
|
|
18
|
+
isInBound(x: number, y: number): boolean;
|
|
19
|
+
getEdges(pos: Position): Position[];
|
|
20
|
+
clone(): BTGridData;
|
|
21
|
+
}
|
|
22
|
+
export declare class IntArray2D {
|
|
23
|
+
private readonly array;
|
|
24
|
+
readonly width: number;
|
|
25
|
+
readonly height: number;
|
|
26
|
+
private constructor();
|
|
27
|
+
static create(width: number, height: number): IntArray2D;
|
|
28
|
+
set(x: number, y: number, value: number): void;
|
|
29
|
+
get(x: number, y: number): number;
|
|
30
|
+
clone(): IntArray2D;
|
|
31
|
+
}
|
|
32
|
+
export interface CheckResult {
|
|
33
|
+
tilesNeedCheck: IntArray2D | null;
|
|
34
|
+
ratings: Rating[] | null;
|
|
35
|
+
}
|
|
36
|
+
export interface Rating {
|
|
37
|
+
pos: Position;
|
|
38
|
+
score: number;
|
|
39
|
+
}
|
|
40
|
+
export default abstract class BTModule {
|
|
41
|
+
abstract checkGlobal(grid: BTGridData): CheckResult | false;
|
|
42
|
+
checkLocal(grid: BTGridData, _: Position[]): CheckResult | boolean;
|
|
43
|
+
}
|
|
44
|
+
export declare function getOppositeColor(color: BTColor): BTColor;
|
|
45
|
+
export declare function colorToBTTile(color: Color): BTTile;
|
|
46
|
+
export declare function createOneTileResult(grid: BTGridData, pos: Position, score?: number | undefined): CheckResult;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Color } from '../../primitives';
|
|
2
|
+
export var BTTile;
|
|
3
|
+
(function (BTTile) {
|
|
4
|
+
BTTile[BTTile["Empty"] = 0] = "Empty";
|
|
5
|
+
BTTile[BTTile["Dark"] = 1] = "Dark";
|
|
6
|
+
BTTile[BTTile["Light"] = 2] = "Light";
|
|
7
|
+
BTTile[BTTile["NonExist"] = 3] = "NonExist";
|
|
8
|
+
})(BTTile || (BTTile = {}));
|
|
9
|
+
export class BTGridData {
|
|
10
|
+
constructor(tiles, connections, modules, width, height) {
|
|
11
|
+
Object.defineProperty(this, "tiles", {
|
|
12
|
+
enumerable: true,
|
|
13
|
+
configurable: true,
|
|
14
|
+
writable: true,
|
|
15
|
+
value: tiles
|
|
16
|
+
});
|
|
17
|
+
Object.defineProperty(this, "connections", {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
configurable: true,
|
|
20
|
+
writable: true,
|
|
21
|
+
value: connections
|
|
22
|
+
});
|
|
23
|
+
Object.defineProperty(this, "modules", {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
configurable: true,
|
|
26
|
+
writable: true,
|
|
27
|
+
value: modules
|
|
28
|
+
});
|
|
29
|
+
Object.defineProperty(this, "width", {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
configurable: true,
|
|
32
|
+
writable: true,
|
|
33
|
+
value: width
|
|
34
|
+
});
|
|
35
|
+
Object.defineProperty(this, "height", {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
configurable: true,
|
|
38
|
+
writable: true,
|
|
39
|
+
value: height
|
|
40
|
+
});
|
|
41
|
+
this.tiles = tiles;
|
|
42
|
+
this.connections = connections;
|
|
43
|
+
this.modules = modules;
|
|
44
|
+
this.width = width;
|
|
45
|
+
this.height = height;
|
|
46
|
+
}
|
|
47
|
+
getTile(x, y) {
|
|
48
|
+
return this.tiles[y][x];
|
|
49
|
+
}
|
|
50
|
+
setTileWithConnection(x, y, tile) {
|
|
51
|
+
for (const pos of this.connections[y][x]) {
|
|
52
|
+
this.tiles[pos.y][pos.x] = tile;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
isInBound(x, y) {
|
|
56
|
+
return x >= 0 && x < this.width && y >= 0 && y < this.height;
|
|
57
|
+
}
|
|
58
|
+
getEdges(pos) {
|
|
59
|
+
const positions = [];
|
|
60
|
+
if (pos.x > 0) {
|
|
61
|
+
if (this.getTile(pos.x - 1, pos.y) !== BTTile.NonExist)
|
|
62
|
+
positions.push({ x: pos.x - 1, y: pos.y });
|
|
63
|
+
}
|
|
64
|
+
if (pos.x + 1 < this.width) {
|
|
65
|
+
if (this.getTile(pos.x + 1, pos.y) !== BTTile.NonExist)
|
|
66
|
+
positions.push({ x: pos.x + 1, y: pos.y });
|
|
67
|
+
}
|
|
68
|
+
if (pos.y > 0) {
|
|
69
|
+
if (this.getTile(pos.x, pos.y - 1) !== BTTile.NonExist)
|
|
70
|
+
positions.push({ x: pos.x, y: pos.y - 1 });
|
|
71
|
+
}
|
|
72
|
+
if (pos.y + 1 < this.height) {
|
|
73
|
+
if (this.getTile(pos.x, pos.y + 1) !== BTTile.NonExist)
|
|
74
|
+
positions.push({ x: pos.x, y: pos.y + 1 });
|
|
75
|
+
}
|
|
76
|
+
return positions;
|
|
77
|
+
}
|
|
78
|
+
clone() {
|
|
79
|
+
return new BTGridData(this.tiles.map(arr => [...arr]), this.connections, this.modules, this.width, this.height);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export class IntArray2D {
|
|
83
|
+
constructor(array, width, height) {
|
|
84
|
+
Object.defineProperty(this, "array", {
|
|
85
|
+
enumerable: true,
|
|
86
|
+
configurable: true,
|
|
87
|
+
writable: true,
|
|
88
|
+
value: array
|
|
89
|
+
});
|
|
90
|
+
Object.defineProperty(this, "width", {
|
|
91
|
+
enumerable: true,
|
|
92
|
+
configurable: true,
|
|
93
|
+
writable: true,
|
|
94
|
+
value: width
|
|
95
|
+
});
|
|
96
|
+
Object.defineProperty(this, "height", {
|
|
97
|
+
enumerable: true,
|
|
98
|
+
configurable: true,
|
|
99
|
+
writable: true,
|
|
100
|
+
value: height
|
|
101
|
+
});
|
|
102
|
+
this.array = array;
|
|
103
|
+
this.width = width;
|
|
104
|
+
this.height = height;
|
|
105
|
+
}
|
|
106
|
+
static create(width, height) {
|
|
107
|
+
return new IntArray2D(new Uint8Array(width * height), width, height);
|
|
108
|
+
}
|
|
109
|
+
set(x, y, value) {
|
|
110
|
+
this.array[y * this.width + x] = value;
|
|
111
|
+
}
|
|
112
|
+
get(x, y) {
|
|
113
|
+
return this.array[y * this.width + x];
|
|
114
|
+
}
|
|
115
|
+
clone() {
|
|
116
|
+
return new IntArray2D(new Uint8Array(this.array), this.width, this.height);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
export default class BTModule {
|
|
120
|
+
checkLocal(grid, _) {
|
|
121
|
+
return this.checkGlobal(grid);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export function getOppositeColor(color) {
|
|
125
|
+
return color === BTTile.Dark ? BTTile.Light : BTTile.Dark;
|
|
126
|
+
}
|
|
127
|
+
export function colorToBTTile(color) {
|
|
128
|
+
if (color === Color.Gray)
|
|
129
|
+
return BTTile.Empty;
|
|
130
|
+
else if (color === Color.Light)
|
|
131
|
+
return BTTile.Light;
|
|
132
|
+
else
|
|
133
|
+
return BTTile.Dark;
|
|
134
|
+
}
|
|
135
|
+
export function createOneTileResult(grid, pos, score = 1) {
|
|
136
|
+
const tilesNeedCheck = IntArray2D.create(grid.width, grid.height);
|
|
137
|
+
tilesNeedCheck.set(pos.x, pos.y, 1);
|
|
138
|
+
const ratings = [{ pos, score }];
|
|
139
|
+
return { tilesNeedCheck, ratings };
|
|
140
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Position } from '../../../primitives';
|
|
2
|
+
import BanPatternRule from '../../../rules/banPatternRule';
|
|
3
|
+
import BTModule, { BTGridData, CheckResult } from '../data';
|
|
4
|
+
export default class BanPatternBTModule extends BTModule {
|
|
5
|
+
instr: BanPatternRule;
|
|
6
|
+
constructor(instr: BanPatternRule);
|
|
7
|
+
checkGlobal(_: BTGridData): CheckResult | false;
|
|
8
|
+
checkLocal(grid: BTGridData, positions: Position[]): CheckResult | false;
|
|
9
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import BTModule, { BTTile, colorToBTTile, } from '../data';
|
|
2
|
+
export default class BanPatternBTModule extends BTModule {
|
|
3
|
+
constructor(instr) {
|
|
4
|
+
super();
|
|
5
|
+
Object.defineProperty(this, "instr", {
|
|
6
|
+
enumerable: true,
|
|
7
|
+
configurable: true,
|
|
8
|
+
writable: true,
|
|
9
|
+
value: void 0
|
|
10
|
+
});
|
|
11
|
+
this.instr = instr;
|
|
12
|
+
}
|
|
13
|
+
checkGlobal(_) {
|
|
14
|
+
// TODO: Impl this properly
|
|
15
|
+
return { tilesNeedCheck: null, ratings: null };
|
|
16
|
+
}
|
|
17
|
+
checkLocal(grid, positions) {
|
|
18
|
+
const ratings = [];
|
|
19
|
+
// TODO: Do not iterate positions
|
|
20
|
+
for (const { x, y } of positions) {
|
|
21
|
+
const tile = grid.getTile(x, y);
|
|
22
|
+
for (const shape of this.instr.cache) {
|
|
23
|
+
for (const origin of shape.elements) {
|
|
24
|
+
if (colorToBTTile(origin.color) !== tile)
|
|
25
|
+
continue;
|
|
26
|
+
if (origin.x > x ||
|
|
27
|
+
origin.y > y ||
|
|
28
|
+
shape.width - origin.x + x > grid.width ||
|
|
29
|
+
shape.height - origin.y + y > grid.height)
|
|
30
|
+
continue;
|
|
31
|
+
// We add a tile into ratings if that tile is the only mismatched tile
|
|
32
|
+
let match = true;
|
|
33
|
+
let mismatchPos = null;
|
|
34
|
+
for (const element of shape.elements) {
|
|
35
|
+
const eleTile = grid.getTile(element.x - origin.x + x, element.y - origin.y + y);
|
|
36
|
+
if (eleTile === BTTile.Empty) {
|
|
37
|
+
if (match) {
|
|
38
|
+
match = false;
|
|
39
|
+
mismatchPos = {
|
|
40
|
+
x: element.x - origin.x + x,
|
|
41
|
+
y: element.y - origin.y + y,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
mismatchPos = null;
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else if (eleTile !== colorToBTTile(element.color)) {
|
|
50
|
+
match = false;
|
|
51
|
+
mismatchPos = null;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (match)
|
|
56
|
+
return false;
|
|
57
|
+
if (mismatchPos)
|
|
58
|
+
ratings.push({ pos: mismatchPos, score: 10000 });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// TODO: Redesign API - This is not the best!
|
|
63
|
+
// Ratings refresh on every backtrack! Ratings should be appended on the previous result instead of over-writing!
|
|
64
|
+
return { tilesNeedCheck: null, ratings };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import CellCountRule from '../../../rules/cellCountRule';
|
|
2
|
+
import BTModule, { BTGridData, CheckResult } from '../data';
|
|
3
|
+
export default class CellCountBTModule extends BTModule {
|
|
4
|
+
instr: CellCountRule;
|
|
5
|
+
constructor(instr: CellCountRule);
|
|
6
|
+
checkGlobal(grid: BTGridData): CheckResult | false;
|
|
7
|
+
}
|