@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.
Files changed (211) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +19 -0
  3. package/assets/logic-core.global.d.ts +5865 -0
  4. package/assets/z3-built.js +14723 -0
  5. package/assets/z3-built.wasm +0 -0
  6. package/assets/z3-built.worker.js +206 -0
  7. package/dist/data/config.d.ts +101 -0
  8. package/dist/data/config.js +55 -0
  9. package/dist/data/configurable.d.ts +12 -0
  10. package/dist/data/configurable.js +26 -0
  11. package/dist/data/dataHelper.d.ts +77 -0
  12. package/dist/data/dataHelper.js +190 -0
  13. package/dist/data/events/eventHelper.d.ts +1 -0
  14. package/dist/data/events/eventHelper.js +6 -0
  15. package/dist/data/events/onFinalValidation.d.ts +14 -0
  16. package/dist/data/events/onFinalValidation.js +4 -0
  17. package/dist/data/events/onGridChange.d.ts +6 -0
  18. package/dist/data/events/onGridChange.js +4 -0
  19. package/dist/data/events/onGridResize.d.ts +9 -0
  20. package/dist/data/events/onGridResize.js +4 -0
  21. package/dist/data/events/onSetGrid.d.ts +6 -0
  22. package/dist/data/events/onSetGrid.js +4 -0
  23. package/dist/data/events/onSymbolValidation.d.ts +18 -0
  24. package/dist/data/events/onSymbolValidation.js +4 -0
  25. package/dist/data/grid.d.ts +362 -0
  26. package/dist/data/grid.js +886 -0
  27. package/dist/data/gridConnections.d.ts +38 -0
  28. package/dist/data/gridConnections.js +328 -0
  29. package/dist/data/instruction.d.ts +19 -0
  30. package/dist/data/instruction.js +23 -0
  31. package/dist/data/primitives.d.ts +85 -0
  32. package/dist/data/primitives.js +90 -0
  33. package/dist/data/puzzle.d.ts +86 -0
  34. package/dist/data/puzzle.js +22 -0
  35. package/dist/data/rules/banPatternRule.d.ts +29 -0
  36. package/dist/data/rules/banPatternRule.js +133 -0
  37. package/dist/data/rules/cellCountRule.d.ts +32 -0
  38. package/dist/data/rules/cellCountRule.js +166 -0
  39. package/dist/data/rules/completePatternRule.d.ts +22 -0
  40. package/dist/data/rules/completePatternRule.js +53 -0
  41. package/dist/data/rules/connectAllRule.d.ts +28 -0
  42. package/dist/data/rules/connectAllRule.js +113 -0
  43. package/dist/data/rules/customRule.d.ts +32 -0
  44. package/dist/data/rules/customRule.js +92 -0
  45. package/dist/data/rules/foresightRule.d.ts +30 -0
  46. package/dist/data/rules/foresightRule.js +107 -0
  47. package/dist/data/rules/index.d.ts +3 -0
  48. package/dist/data/rules/index.js +10 -0
  49. package/dist/data/rules/musicControlLine.d.ts +64 -0
  50. package/dist/data/rules/musicControlLine.js +178 -0
  51. package/dist/data/rules/musicGridRule.d.ts +46 -0
  52. package/dist/data/rules/musicGridRule.js +211 -0
  53. package/dist/data/rules/mysteryRule.d.ts +37 -0
  54. package/dist/data/rules/mysteryRule.js +164 -0
  55. package/dist/data/rules/offByXRule.d.ts +30 -0
  56. package/dist/data/rules/offByXRule.js +134 -0
  57. package/dist/data/rules/regionAreaRule.d.ts +33 -0
  58. package/dist/data/rules/regionAreaRule.js +182 -0
  59. package/dist/data/rules/regionShapeRule.d.ts +22 -0
  60. package/dist/data/rules/regionShapeRule.js +58 -0
  61. package/dist/data/rules/rule.d.ts +18 -0
  62. package/dist/data/rules/rule.js +19 -0
  63. package/dist/data/rules/rules.gen.d.ts +14 -0
  64. package/dist/data/rules/rules.gen.js +18 -0
  65. package/dist/data/rules/sameShapeRule.d.ts +27 -0
  66. package/dist/data/rules/sameShapeRule.js +88 -0
  67. package/dist/data/rules/symbolsPerRegionRule.d.ts +37 -0
  68. package/dist/data/rules/symbolsPerRegionRule.js +211 -0
  69. package/dist/data/rules/undercluedRule.d.ts +22 -0
  70. package/dist/data/rules/undercluedRule.js +60 -0
  71. package/dist/data/rules/uniqueShapeRule.d.ts +27 -0
  72. package/dist/data/rules/uniqueShapeRule.js +85 -0
  73. package/dist/data/serializer/allSerializers.d.ts +30 -0
  74. package/dist/data/serializer/allSerializers.js +64 -0
  75. package/dist/data/serializer/compressor/allCompressors.d.ts +14 -0
  76. package/dist/data/serializer/compressor/allCompressors.js +43 -0
  77. package/dist/data/serializer/compressor/compressorBase.d.ts +16 -0
  78. package/dist/data/serializer/compressor/compressorBase.js +2 -0
  79. package/dist/data/serializer/compressor/deflateCompressor.d.ts +7 -0
  80. package/dist/data/serializer/compressor/deflateCompressor.js +17 -0
  81. package/dist/data/serializer/compressor/gzipCompressor.d.ts +5 -0
  82. package/dist/data/serializer/compressor/gzipCompressor.js +9 -0
  83. package/dist/data/serializer/compressor/streamCompressor.d.ts +6 -0
  84. package/dist/data/serializer/compressor/streamCompressor.js +36 -0
  85. package/dist/data/serializer/serializerBase.d.ts +27 -0
  86. package/dist/data/serializer/serializerBase.js +2 -0
  87. package/dist/data/serializer/serializer_v0.d.ts +36 -0
  88. package/dist/data/serializer/serializer_v0.js +426 -0
  89. package/dist/data/shapes.d.ts +17 -0
  90. package/dist/data/shapes.js +117 -0
  91. package/dist/data/solver/allSolvers.d.ts +3 -0
  92. package/dist/data/solver/allSolvers.js +11 -0
  93. package/dist/data/solver/backtrack/backtrackSolver.d.ts +9 -0
  94. package/dist/data/solver/backtrack/backtrackSolver.js +92 -0
  95. package/dist/data/solver/backtrack/backtrackWorker.d.ts +2 -0
  96. package/dist/data/solver/backtrack/backtrackWorker.js +295 -0
  97. package/dist/data/solver/backtrack/data.d.ts +46 -0
  98. package/dist/data/solver/backtrack/data.js +140 -0
  99. package/dist/data/solver/backtrack/rules/banPattern.d.ts +9 -0
  100. package/dist/data/solver/backtrack/rules/banPattern.js +66 -0
  101. package/dist/data/solver/backtrack/rules/cellCount.d.ts +7 -0
  102. package/dist/data/solver/backtrack/rules/cellCount.js +30 -0
  103. package/dist/data/solver/backtrack/rules/connectAll.d.ts +7 -0
  104. package/dist/data/solver/backtrack/rules/connectAll.js +49 -0
  105. package/dist/data/solver/backtrack/rules/regionArea.d.ts +8 -0
  106. package/dist/data/solver/backtrack/rules/regionArea.js +76 -0
  107. package/dist/data/solver/backtrack/rules/regionShape.d.ts +8 -0
  108. package/dist/data/solver/backtrack/rules/regionShape.js +62 -0
  109. package/dist/data/solver/backtrack/rules/sameShape.d.ts +8 -0
  110. package/dist/data/solver/backtrack/rules/sameShape.js +19 -0
  111. package/dist/data/solver/backtrack/rules/symbolsPerRegion.d.ts +10 -0
  112. package/dist/data/solver/backtrack/rules/symbolsPerRegion.js +92 -0
  113. package/dist/data/solver/backtrack/rules/uniqueShape.d.ts +8 -0
  114. package/dist/data/solver/backtrack/rules/uniqueShape.js +19 -0
  115. package/dist/data/solver/backtrack/symbols/areaNumber.d.ts +9 -0
  116. package/dist/data/solver/backtrack/symbols/areaNumber.js +77 -0
  117. package/dist/data/solver/backtrack/symbols/dart.d.ts +9 -0
  118. package/dist/data/solver/backtrack/symbols/dart.js +58 -0
  119. package/dist/data/solver/backtrack/symbols/directionLinker.d.ts +9 -0
  120. package/dist/data/solver/backtrack/symbols/directionLinker.js +50 -0
  121. package/dist/data/solver/backtrack/symbols/galaxy.d.ts +9 -0
  122. package/dist/data/solver/backtrack/symbols/galaxy.js +19 -0
  123. package/dist/data/solver/backtrack/symbols/letter.d.ts +9 -0
  124. package/dist/data/solver/backtrack/symbols/letter.js +100 -0
  125. package/dist/data/solver/backtrack/symbols/lotus.d.ts +9 -0
  126. package/dist/data/solver/backtrack/symbols/lotus.js +36 -0
  127. package/dist/data/solver/backtrack/symbols/minesweeper.d.ts +9 -0
  128. package/dist/data/solver/backtrack/symbols/minesweeper.js +55 -0
  129. package/dist/data/solver/backtrack/symbols/myopia.d.ts +7 -0
  130. package/dist/data/solver/backtrack/symbols/myopia.js +79 -0
  131. package/dist/data/solver/backtrack/symbols/viewpoint.d.ts +7 -0
  132. package/dist/data/solver/backtrack/symbols/viewpoint.js +56 -0
  133. package/dist/data/solver/solver.d.ts +61 -0
  134. package/dist/data/solver/solver.js +55 -0
  135. package/dist/data/solver/underclued/undercluedSolver.d.ts +8 -0
  136. package/dist/data/solver/underclued/undercluedSolver.js +55 -0
  137. package/dist/data/solver/underclued/undercluedWorker.d.ts +2 -0
  138. package/dist/data/solver/underclued/undercluedWorker.js +131 -0
  139. package/dist/data/solver/z3/modules/areaNumberModule.d.ts +9 -0
  140. package/dist/data/solver/z3/modules/areaNumberModule.js +35 -0
  141. package/dist/data/solver/z3/modules/cellCountModule.d.ts +9 -0
  142. package/dist/data/solver/z3/modules/cellCountModule.js +59 -0
  143. package/dist/data/solver/z3/modules/connectAllModule.d.ts +9 -0
  144. package/dist/data/solver/z3/modules/connectAllModule.js +32 -0
  145. package/dist/data/solver/z3/modules/dartModule.d.ts +9 -0
  146. package/dist/data/solver/z3/modules/dartModule.js +69 -0
  147. package/dist/data/solver/z3/modules/index.d.ts +3 -0
  148. package/dist/data/solver/z3/modules/index.js +10 -0
  149. package/dist/data/solver/z3/modules/letterModule.d.ts +9 -0
  150. package/dist/data/solver/z3/modules/letterModule.js +41 -0
  151. package/dist/data/solver/z3/modules/modules.gen.d.ts +8 -0
  152. package/dist/data/solver/z3/modules/modules.gen.js +12 -0
  153. package/dist/data/solver/z3/modules/myopiaModule.d.ts +9 -0
  154. package/dist/data/solver/z3/modules/myopiaModule.js +64 -0
  155. package/dist/data/solver/z3/modules/regionAreaModule.d.ts +9 -0
  156. package/dist/data/solver/z3/modules/regionAreaModule.js +48 -0
  157. package/dist/data/solver/z3/modules/viewpointModule.d.ts +9 -0
  158. package/dist/data/solver/z3/modules/viewpointModule.js +37 -0
  159. package/dist/data/solver/z3/modules/z3Module.d.ts +7 -0
  160. package/dist/data/solver/z3/modules/z3Module.js +3 -0
  161. package/dist/data/solver/z3/utils.d.ts +2 -0
  162. package/dist/data/solver/z3/utils.js +26 -0
  163. package/dist/data/solver/z3/z3Solver.d.ts +10 -0
  164. package/dist/data/solver/z3/z3Solver.js +134 -0
  165. package/dist/data/solver/z3/z3SolverContext.d.ts +808 -0
  166. package/dist/data/solver/z3/z3SolverContext.js +49 -0
  167. package/dist/data/symbols/areaNumberSymbol.d.ts +30 -0
  168. package/dist/data/symbols/areaNumberSymbol.js +88 -0
  169. package/dist/data/symbols/customIconSymbol.d.ts +35 -0
  170. package/dist/data/symbols/customIconSymbol.js +105 -0
  171. package/dist/data/symbols/customSymbol.d.ts +23 -0
  172. package/dist/data/symbols/customSymbol.js +48 -0
  173. package/dist/data/symbols/customTextSymbol.d.ts +33 -0
  174. package/dist/data/symbols/customTextSymbol.js +106 -0
  175. package/dist/data/symbols/dartSymbol.d.ts +35 -0
  176. package/dist/data/symbols/dartSymbol.js +110 -0
  177. package/dist/data/symbols/directionLinkerSymbol.d.ts +36 -0
  178. package/dist/data/symbols/directionLinkerSymbol.js +259 -0
  179. package/dist/data/symbols/galaxySymbol.d.ts +26 -0
  180. package/dist/data/symbols/galaxySymbol.js +74 -0
  181. package/dist/data/symbols/index.d.ts +3 -0
  182. package/dist/data/symbols/index.js +10 -0
  183. package/dist/data/symbols/letterSymbol.d.ts +31 -0
  184. package/dist/data/symbols/letterSymbol.js +137 -0
  185. package/dist/data/symbols/lotusSymbol.d.ts +29 -0
  186. package/dist/data/symbols/lotusSymbol.js +132 -0
  187. package/dist/data/symbols/minesweeperSymbol.d.ts +31 -0
  188. package/dist/data/symbols/minesweeperSymbol.js +100 -0
  189. package/dist/data/symbols/multiEntrySymbol.d.ts +11 -0
  190. package/dist/data/symbols/multiEntrySymbol.js +14 -0
  191. package/dist/data/symbols/myopiaSymbol.d.ts +34 -0
  192. package/dist/data/symbols/myopiaSymbol.js +187 -0
  193. package/dist/data/symbols/numberSymbol.d.ts +19 -0
  194. package/dist/data/symbols/numberSymbol.js +41 -0
  195. package/dist/data/symbols/symbol.d.ts +16 -0
  196. package/dist/data/symbols/symbol.js +51 -0
  197. package/dist/data/symbols/symbols.gen.d.ts +10 -0
  198. package/dist/data/symbols/symbols.gen.js +14 -0
  199. package/dist/data/symbols/viewpointSymbol.d.ts +31 -0
  200. package/dist/data/symbols/viewpointSymbol.js +106 -0
  201. package/dist/data/tile.d.ts +26 -0
  202. package/dist/data/tile.js +68 -0
  203. package/dist/data/tileConnections.d.ts +25 -0
  204. package/dist/data/tileConnections.js +74 -0
  205. package/dist/data/validate.d.ts +5 -0
  206. package/dist/data/validate.js +131 -0
  207. package/dist/index.d.ts +96 -0
  208. package/dist/index.js +100 -0
  209. package/dist/polyfill/streamPolyfill.d.ts +2 -0
  210. package/dist/polyfill/streamPolyfill.js +1 -0
  211. 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,2 @@
1
+ declare const _default: null;
2
+ export default _default;
@@ -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
+ }