@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,211 @@
1
+ import Rule from './rule';
2
+ import GridData from '../grid';
3
+ import { ConfigType } from '../config';
4
+ import { Color, Comparison, State } from '../primitives';
5
+ import { array } from '../dataHelper';
6
+ import LetterSymbol from '../symbols/letterSymbol';
7
+ import GridConnections from '../gridConnections';
8
+ class SymbolsPerRegionRule extends Rule {
9
+ /**
10
+ * **Exactly <count> symbols per <color> area**
11
+ *
12
+ * @param color - Color of the region affected by the rule
13
+ * @param count - Number of symbols to have in each region
14
+ * @param comparison - Comparison to use when checking the number of symbols
15
+ */
16
+ constructor(color, count, comparison = Comparison.Equal) {
17
+ super();
18
+ Object.defineProperty(this, "color", {
19
+ enumerable: true,
20
+ configurable: true,
21
+ writable: true,
22
+ value: color
23
+ });
24
+ Object.defineProperty(this, "count", {
25
+ enumerable: true,
26
+ configurable: true,
27
+ writable: true,
28
+ value: count
29
+ });
30
+ Object.defineProperty(this, "comparison", {
31
+ enumerable: true,
32
+ configurable: true,
33
+ writable: true,
34
+ value: comparison
35
+ });
36
+ this.color = color;
37
+ this.count = count;
38
+ this.comparison = comparison;
39
+ }
40
+ get id() {
41
+ return `symbols_per_region`;
42
+ }
43
+ get explanation() {
44
+ switch (this.comparison) {
45
+ case Comparison.AtLeast:
46
+ return `At least ${this.count} symbol${this.count === 1 ? '' : 's'} per ${this.color} area`;
47
+ case Comparison.AtMost:
48
+ return `At most ${this.count} symbol${this.count === 1 ? '' : 's'} per ${this.color} area`;
49
+ default:
50
+ return `Exactly ${this.count} symbol${this.count === 1 ? '' : 's'} per ${this.color} area`;
51
+ }
52
+ }
53
+ get configs() {
54
+ return SymbolsPerRegionRule.CONFIGS;
55
+ }
56
+ createExampleGrid() {
57
+ if (this.count > SymbolsPerRegionRule.SYMBOL_POSITIONS.length ||
58
+ this.comparison !== Comparison.Equal) {
59
+ let description = '';
60
+ switch (this.comparison) {
61
+ case Comparison.AtLeast:
62
+ description = `≥${this.count}X`;
63
+ break;
64
+ case Comparison.AtMost:
65
+ description = `≤${this.count}X`;
66
+ break;
67
+ default:
68
+ description = `${this.count}X`;
69
+ break;
70
+ }
71
+ const symbol = new LetterSymbol(1.5, 1.5, description);
72
+ return SymbolsPerRegionRule.EXAMPLE_GRIDS[this.color]
73
+ .addSymbol(symbol)
74
+ .withConnections(GridConnections.create(['.....', '.aa..', '.aa..', '.....']));
75
+ }
76
+ const symbols = [];
77
+ for (let i = 0; i < this.count; i++) {
78
+ const { x, y } = SymbolsPerRegionRule.SYMBOL_POSITIONS[i];
79
+ symbols.push(new LetterSymbol(x, y, 'X'));
80
+ }
81
+ return SymbolsPerRegionRule.EXAMPLE_GRIDS[this.color].withSymbols(symbols);
82
+ }
83
+ get searchVariants() {
84
+ return SymbolsPerRegionRule.SEARCH_VARIANTS;
85
+ }
86
+ validateGrid(grid) {
87
+ const visited = array(grid.width, grid.height, (i, j) => !(grid.getTile(i, j).exists && grid.getTile(i, j).color === this.color));
88
+ let complete = true;
89
+ while (true) {
90
+ const seed = grid.find((_tile, x, y) => !visited[y][x]);
91
+ if (!seed)
92
+ break;
93
+ const completed = [];
94
+ const gray = [];
95
+ let nbSymbolsIn = 0;
96
+ grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === this.color, (_, x, y) => {
97
+ completed.push({ x, y });
98
+ visited[y][x] = true;
99
+ nbSymbolsIn += SymbolsPerRegionRule.countAllSymbolsOfPosition(grid, x, y);
100
+ });
101
+ if (this.comparison !== Comparison.AtLeast && nbSymbolsIn > this.count) {
102
+ return { state: State.Error, positions: completed };
103
+ }
104
+ let nbSymbolsOut = 0;
105
+ if (this.color === Color.Gray) {
106
+ gray.push(...completed);
107
+ nbSymbolsOut = nbSymbolsIn;
108
+ }
109
+ else {
110
+ grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === Color.Gray || tile.color === this.color, (_, x, y) => {
111
+ gray.push({ x, y });
112
+ nbSymbolsOut += SymbolsPerRegionRule.countAllSymbolsOfPosition(grid, x, y);
113
+ });
114
+ }
115
+ if (this.comparison !== Comparison.AtMost && nbSymbolsOut < this.count) {
116
+ return { state: State.Error, positions: gray };
117
+ }
118
+ if (gray.length !== completed.length) {
119
+ complete = false;
120
+ }
121
+ }
122
+ return complete ? { state: State.Satisfied } : { state: State.Incomplete };
123
+ }
124
+ copyWith({ count, color, comparison, }) {
125
+ return new SymbolsPerRegionRule(color ?? this.color, count ?? this.count, comparison ?? this.comparison);
126
+ }
127
+ withColor(color) {
128
+ return this.copyWith({ color });
129
+ }
130
+ withCount(count) {
131
+ return this.copyWith({ count });
132
+ }
133
+ withComparison(comparison) {
134
+ return this.copyWith({ comparison });
135
+ }
136
+ static countAllSymbolsOfPosition(grid, x, y) {
137
+ let count = 0;
138
+ for (const symbolKind of grid.symbols.values()) {
139
+ if (symbolKind.some(symbol => Math.floor(symbol.x) === x && Math.floor(symbol.y) === y)) {
140
+ count++;
141
+ }
142
+ }
143
+ return count;
144
+ }
145
+ }
146
+ Object.defineProperty(SymbolsPerRegionRule, "SYMBOL_POSITIONS", {
147
+ enumerable: true,
148
+ configurable: true,
149
+ writable: true,
150
+ value: [
151
+ { x: 1, y: 1 },
152
+ { x: 2, y: 2 },
153
+ { x: 2, y: 1 },
154
+ { x: 3, y: 1 },
155
+ { x: 1, y: 2 },
156
+ ]
157
+ });
158
+ Object.defineProperty(SymbolsPerRegionRule, "CONFIGS", {
159
+ enumerable: true,
160
+ configurable: true,
161
+ writable: true,
162
+ value: Object.freeze([
163
+ {
164
+ type: ConfigType.Number,
165
+ default: 1,
166
+ field: 'count',
167
+ description: 'Count',
168
+ configurable: true,
169
+ },
170
+ {
171
+ type: ConfigType.Color,
172
+ default: Color.Light,
173
+ field: 'color',
174
+ description: 'Color',
175
+ configurable: true,
176
+ allowGray: true,
177
+ },
178
+ {
179
+ type: ConfigType.Comparison,
180
+ default: Comparison.Equal,
181
+ field: 'comparison',
182
+ description: 'Comparison',
183
+ configurable: true,
184
+ },
185
+ ])
186
+ });
187
+ Object.defineProperty(SymbolsPerRegionRule, "EXAMPLE_GRIDS", {
188
+ enumerable: true,
189
+ configurable: true,
190
+ writable: true,
191
+ value: {
192
+ [Color.Dark]: GridData.create(['wwwww', 'wbbbw', 'wbbww', 'wwwww']),
193
+ [Color.Light]: GridData.create(['bbbbb', 'bwwwb', 'bwwbb', 'bbbbb']),
194
+ [Color.Gray]: GridData.create(['bwbwb', 'wnnnw', 'bnnwb', 'wbwbw']),
195
+ }
196
+ });
197
+ Object.defineProperty(SymbolsPerRegionRule, "SEARCH_VARIANTS", {
198
+ enumerable: true,
199
+ configurable: true,
200
+ writable: true,
201
+ value: [
202
+ new SymbolsPerRegionRule(Color.Light, 1).searchVariant(),
203
+ new SymbolsPerRegionRule(Color.Dark, 1).searchVariant(),
204
+ new SymbolsPerRegionRule(Color.Light, 1, Comparison.AtLeast).searchVariant(),
205
+ new SymbolsPerRegionRule(Color.Dark, 1, Comparison.AtLeast).searchVariant(),
206
+ new SymbolsPerRegionRule(Color.Light, 1, Comparison.AtMost).searchVariant(),
207
+ new SymbolsPerRegionRule(Color.Dark, 1, Comparison.AtMost).searchVariant(),
208
+ ]
209
+ });
210
+ export default SymbolsPerRegionRule;
211
+ export const instance = new SymbolsPerRegionRule(Color.Dark, 1);
@@ -0,0 +1,22 @@
1
+ import GridData from '../grid';
2
+ import { RuleState } from '../primitives';
3
+ import Rule, { SearchVariant } from './rule';
4
+ export default class UndercluedRule extends Rule {
5
+ private static readonly EXAMPLE_GRID;
6
+ private static readonly SEARCH_VARIANTS;
7
+ /**
8
+ * **Underclued Grid: Mark only what is definitely true**
9
+ *
10
+ * This rule validates answers based on the provided solution.
11
+ */
12
+ constructor();
13
+ get id(): string;
14
+ get explanation(): string;
15
+ createExampleGrid(): GridData;
16
+ get searchVariants(): SearchVariant[];
17
+ validateGrid(_grid: GridData): RuleState;
18
+ copyWith(_: object): this;
19
+ get validateWithSolution(): boolean;
20
+ get isSingleton(): boolean;
21
+ }
22
+ export declare const instance: UndercluedRule;
@@ -0,0 +1,60 @@
1
+ import GridData from '../grid';
2
+ import { State } from '../primitives';
3
+ import AreaNumberSymbol from '../symbols/areaNumberSymbol';
4
+ import CustomTextSymbol from '../symbols/customTextSymbol';
5
+ import Rule from './rule';
6
+ class UndercluedRule extends Rule {
7
+ /**
8
+ * **Underclued Grid: Mark only what is definitely true**
9
+ *
10
+ * This rule validates answers based on the provided solution.
11
+ */
12
+ constructor() {
13
+ super();
14
+ }
15
+ get id() {
16
+ return `underclued`;
17
+ }
18
+ get explanation() {
19
+ return `*Underclued Grid:* Mark only what is definitely true`;
20
+ }
21
+ createExampleGrid() {
22
+ return UndercluedRule.EXAMPLE_GRID;
23
+ }
24
+ get searchVariants() {
25
+ return UndercluedRule.SEARCH_VARIANTS;
26
+ }
27
+ validateGrid(_grid) {
28
+ return { state: State.Incomplete };
29
+ }
30
+ copyWith(_) {
31
+ return new UndercluedRule();
32
+ }
33
+ get validateWithSolution() {
34
+ return true;
35
+ }
36
+ get isSingleton() {
37
+ return true;
38
+ }
39
+ }
40
+ Object.defineProperty(UndercluedRule, "EXAMPLE_GRID", {
41
+ enumerable: true,
42
+ configurable: true,
43
+ writable: true,
44
+ value: Object.freeze(GridData.create(['nbnnn', 'bwbnn', 'nbnnn', 'wwwnn'])
45
+ .addSymbol(new AreaNumberSymbol(1, 1, 1))
46
+ .addSymbol(new AreaNumberSymbol(0, 3, 4))
47
+ .addSymbol(new CustomTextSymbol('', GridData.create([]), 0, 2, '?'))
48
+ .addSymbol(new CustomTextSymbol('', GridData.create([]), 2, 2, '?'))
49
+ .addSymbol(new CustomTextSymbol('', GridData.create([]), 3, 3, '?')))
50
+ });
51
+ Object.defineProperty(UndercluedRule, "SEARCH_VARIANTS", {
52
+ enumerable: true,
53
+ configurable: true,
54
+ writable: true,
55
+ value: [
56
+ new UndercluedRule().searchVariant(),
57
+ ]
58
+ });
59
+ export default UndercluedRule;
60
+ export const instance = new UndercluedRule();
@@ -0,0 +1,27 @@
1
+ import { AnyConfig } from '../config';
2
+ import GridData from '../grid';
3
+ import { Color, RuleState } from '../primitives';
4
+ import RegionShapeRule from './regionShapeRule';
5
+ import { SearchVariant } from './rule';
6
+ export default class UniqueShapeRule extends RegionShapeRule {
7
+ private static readonly CONFIGS;
8
+ private static readonly EXAMPLE_GRID_LIGHT;
9
+ private static readonly EXAMPLE_GRID_DARK;
10
+ private static readonly SEARCH_VARIANTS;
11
+ /**
12
+ * **No two &lt;color&gt; areas have the same shape and size**
13
+ *
14
+ * @param color - The color of the regions to compare.
15
+ */
16
+ constructor(color: Color);
17
+ get id(): string;
18
+ get explanation(): string;
19
+ get configs(): readonly AnyConfig[] | null;
20
+ createExampleGrid(): GridData;
21
+ get searchVariants(): SearchVariant[];
22
+ validateGrid(grid: GridData): RuleState;
23
+ copyWith({ color }: {
24
+ color?: Color;
25
+ }): this;
26
+ }
27
+ export declare const instance: UniqueShapeRule;
@@ -0,0 +1,85 @@
1
+ import { ConfigType } from '../config';
2
+ import GridData from '../grid';
3
+ import { Color, State } from '../primitives';
4
+ import RegionShapeRule from './regionShapeRule';
5
+ class UniqueShapeRule extends RegionShapeRule {
6
+ /**
7
+ * **No two &lt;color&gt; areas have the same shape and size**
8
+ *
9
+ * @param color - The color of the regions to compare.
10
+ */
11
+ constructor(color) {
12
+ super(color);
13
+ }
14
+ get id() {
15
+ return `unique_shape`;
16
+ }
17
+ get explanation() {
18
+ return `No two ${this.color} areas have the same shape and size`;
19
+ }
20
+ get configs() {
21
+ return UniqueShapeRule.CONFIGS;
22
+ }
23
+ createExampleGrid() {
24
+ return this.color === Color.Light
25
+ ? UniqueShapeRule.EXAMPLE_GRID_LIGHT
26
+ : UniqueShapeRule.EXAMPLE_GRID_DARK;
27
+ }
28
+ get searchVariants() {
29
+ return UniqueShapeRule.SEARCH_VARIANTS;
30
+ }
31
+ validateGrid(grid) {
32
+ const { regions, complete } = this.getShapeRegions(grid);
33
+ const errorRegion = regions.find(r => r.count > 1);
34
+ if (errorRegion) {
35
+ return {
36
+ state: State.Error,
37
+ positions: errorRegion.positions,
38
+ };
39
+ }
40
+ else {
41
+ return { state: complete ? State.Satisfied : State.Incomplete };
42
+ }
43
+ }
44
+ copyWith({ color }) {
45
+ return new UniqueShapeRule(color ?? this.color);
46
+ }
47
+ }
48
+ Object.defineProperty(UniqueShapeRule, "CONFIGS", {
49
+ enumerable: true,
50
+ configurable: true,
51
+ writable: true,
52
+ value: Object.freeze([
53
+ {
54
+ type: ConfigType.Color,
55
+ default: Color.Light,
56
+ allowGray: false,
57
+ field: 'color',
58
+ description: 'Color',
59
+ configurable: true,
60
+ },
61
+ ])
62
+ });
63
+ Object.defineProperty(UniqueShapeRule, "EXAMPLE_GRID_LIGHT", {
64
+ enumerable: true,
65
+ configurable: true,
66
+ writable: true,
67
+ value: Object.freeze(GridData.create(['bwbww', 'wwbww', 'wbwbb', 'bwwbw']))
68
+ });
69
+ Object.defineProperty(UniqueShapeRule, "EXAMPLE_GRID_DARK", {
70
+ enumerable: true,
71
+ configurable: true,
72
+ writable: true,
73
+ value: Object.freeze(GridData.create(['wbwbb', 'bbwbb', 'bwbww', 'wbbwb']))
74
+ });
75
+ Object.defineProperty(UniqueShapeRule, "SEARCH_VARIANTS", {
76
+ enumerable: true,
77
+ configurable: true,
78
+ writable: true,
79
+ value: [
80
+ new UniqueShapeRule(Color.Light).searchVariant(),
81
+ new UniqueShapeRule(Color.Dark).searchVariant(),
82
+ ]
83
+ });
84
+ export default UniqueShapeRule;
85
+ export const instance = new UniqueShapeRule(Color.Dark);
@@ -0,0 +1,30 @@
1
+ import GridData from '../grid';
2
+ import { Puzzle } from '../puzzle';
3
+ import Rule from '../rules/rule';
4
+ import Symbol from '../symbols/symbol';
5
+ /**
6
+ * The master serializer for puzzles.
7
+ *
8
+ * It uses the default serializer when stringifying puzzles, and select the correct deserializer when parsing puzzles.
9
+ */
10
+ declare const Serializer: {
11
+ stringifyRule(rule: Rule): string;
12
+ parseRule(input: string): Rule;
13
+ stringifySymbol(symbol: Symbol): string;
14
+ parseSymbol(input: string): Symbol;
15
+ stringifyGrid(grid: GridData): string;
16
+ parseGrid(input: string): GridData;
17
+ /**
18
+ * Convert a puzzle to a string.
19
+ * @param puzzle The puzzle to convert.
20
+ * @returns The string representation of the puzzle.
21
+ */
22
+ stringifyPuzzle(puzzle: Puzzle): string;
23
+ /**
24
+ * Parse a puzzle from a string.
25
+ * @param input The string to parse.
26
+ * @returns The parsed puzzle.
27
+ */
28
+ parsePuzzle(input: string): Puzzle;
29
+ };
30
+ export { Serializer };
@@ -0,0 +1,64 @@
1
+ import SerializerV0 from './serializer_v0';
2
+ const allSerializers = new Map();
3
+ function register(prototype) {
4
+ allSerializers.set(prototype.version, prototype);
5
+ }
6
+ let defaultSerializer;
7
+ register((defaultSerializer = new SerializerV0()));
8
+ function selectSerializer(input) {
9
+ const match = input.match(/^(\d+)_/);
10
+ const version = match ? parseInt(match[1]) : 0;
11
+ const serializer = allSerializers.get(version);
12
+ if (serializer) {
13
+ return { serializer, data: input.slice(match?.[0].length ?? 0) };
14
+ }
15
+ else {
16
+ throw new Error(`Unknown serializer version for ${input}`);
17
+ }
18
+ }
19
+ /**
20
+ * The master serializer for puzzles.
21
+ *
22
+ * It uses the default serializer when stringifying puzzles, and select the correct deserializer when parsing puzzles.
23
+ */
24
+ const Serializer = {
25
+ stringifyRule(rule) {
26
+ return `${defaultSerializer.version}_${defaultSerializer.stringifyRule(rule)}`;
27
+ },
28
+ parseRule(input) {
29
+ const { serializer, data } = selectSerializer(input);
30
+ return serializer.parseRule(data);
31
+ },
32
+ stringifySymbol(symbol) {
33
+ return `${defaultSerializer.version}_${defaultSerializer.stringifySymbol(symbol)}`;
34
+ },
35
+ parseSymbol(input) {
36
+ const { serializer, data } = selectSerializer(input);
37
+ return serializer.parseSymbol(data);
38
+ },
39
+ stringifyGrid(grid) {
40
+ return `${defaultSerializer.version}_${defaultSerializer.stringifyGrid(grid)}`;
41
+ },
42
+ parseGrid(input) {
43
+ const { serializer, data } = selectSerializer(input);
44
+ return serializer.parseGrid(data);
45
+ },
46
+ /**
47
+ * Convert a puzzle to a string.
48
+ * @param puzzle The puzzle to convert.
49
+ * @returns The string representation of the puzzle.
50
+ */
51
+ stringifyPuzzle(puzzle) {
52
+ return `${defaultSerializer.version}_${defaultSerializer.stringifyPuzzle(puzzle)}`;
53
+ },
54
+ /**
55
+ * Parse a puzzle from a string.
56
+ * @param input The string to parse.
57
+ * @returns The parsed puzzle.
58
+ */
59
+ parsePuzzle(input) {
60
+ const { serializer, data } = selectSerializer(input);
61
+ return serializer.parsePuzzle(data);
62
+ },
63
+ };
64
+ export { Serializer };
@@ -0,0 +1,14 @@
1
+ import CompressorBase from './compressorBase';
2
+ /**
3
+ * The master compressor for compressing and decompressing strings.
4
+ *
5
+ * It compares the output of multiple compressors and selects the one with the smallest size (slow),
6
+ * and selects the correct decompressor when decompressing.
7
+ */
8
+ declare class MasterCompressor extends CompressorBase {
9
+ get id(): string;
10
+ compress(input: string): Promise<string>;
11
+ decompress(input: string): Promise<string>;
12
+ }
13
+ declare const Compressor: MasterCompressor;
14
+ export { Compressor };
@@ -0,0 +1,43 @@
1
+ import { minBy } from '../../dataHelper';
2
+ import CompressorBase from './compressorBase';
3
+ import GzipCompressor from './gzipCompressor';
4
+ import DeflateCompressor from './deflateCompressor';
5
+ const allCompressors = new Map();
6
+ function register(prototype) {
7
+ allCompressors.set(prototype.id, prototype);
8
+ }
9
+ const activeCompressors = [new DeflateCompressor()];
10
+ activeCompressors.forEach(register);
11
+ register(new GzipCompressor());
12
+ /**
13
+ * The master compressor for compressing and decompressing strings.
14
+ *
15
+ * It compares the output of multiple compressors and selects the one with the smallest size (slow),
16
+ * and selects the correct decompressor when decompressing.
17
+ */
18
+ class MasterCompressor extends CompressorBase {
19
+ get id() {
20
+ return `compressor`;
21
+ }
22
+ async compress(input) {
23
+ const compressed = await Promise.all(activeCompressors.map(async (compressor) => `${compressor.id}_${await compressor.compress(input)}`));
24
+ return minBy(compressed, c => encodeURIComponent(c).length) ?? '';
25
+ }
26
+ async decompress(input) {
27
+ const match = input.match(/^([^_]+?)_(.+)$/);
28
+ let compressorId;
29
+ let compressed;
30
+ if (match) {
31
+ compressorId = match[1];
32
+ compressed = match[2];
33
+ }
34
+ else {
35
+ compressorId = 'gzip';
36
+ compressed = input;
37
+ }
38
+ const compressor = allCompressors.get(compressorId);
39
+ return compressor.decompress(compressed);
40
+ }
41
+ }
42
+ const Compressor = new MasterCompressor();
43
+ export { Compressor };
@@ -0,0 +1,16 @@
1
+ export default abstract class CompressorBase {
2
+ /**
3
+ * The unique identifier for this compressor.
4
+ */
5
+ abstract get id(): string;
6
+ /**
7
+ * Compress the given input string into URL-safe compressed string.
8
+ * @param input The input string to compress.
9
+ */
10
+ abstract compress(input: string): Promise<string>;
11
+ /**
12
+ * Decompress the given compressed string back into the original string.
13
+ * @param input The compressed string to decompress.
14
+ */
15
+ abstract decompress(input: string): Promise<string>;
16
+ }
@@ -0,0 +1,2 @@
1
+ export default class CompressorBase {
2
+ }
@@ -0,0 +1,7 @@
1
+ import StreamCompressor from './streamCompressor';
2
+ export default class DeflateCompressor extends StreamCompressor {
3
+ get id(): string;
4
+ protected get algorithm(): CompressionFormat;
5
+ compress(input: string): Promise<string>;
6
+ decompress(input: string): Promise<string>;
7
+ }
@@ -0,0 +1,17 @@
1
+ import StreamCompressor from './streamCompressor';
2
+ export default class DeflateCompressor extends StreamCompressor {
3
+ get id() {
4
+ return `dfl`;
5
+ }
6
+ get algorithm() {
7
+ return 'deflate-raw';
8
+ }
9
+ async compress(input) {
10
+ const result = await super.compress(input);
11
+ return result.replace(/\+/g, '-').replace(/\//g, '_');
12
+ }
13
+ async decompress(input) {
14
+ input = input.replace(/-/g, '+').replace(/_/g, '/');
15
+ return await super.decompress(input);
16
+ }
17
+ }
@@ -0,0 +1,5 @@
1
+ import StreamCompressor from './streamCompressor';
2
+ export default class GzipCompressor extends StreamCompressor {
3
+ get id(): string;
4
+ protected get algorithm(): CompressionFormat;
5
+ }
@@ -0,0 +1,9 @@
1
+ import StreamCompressor from './streamCompressor';
2
+ export default class GzipCompressor extends StreamCompressor {
3
+ get id() {
4
+ return `gzip`;
5
+ }
6
+ get algorithm() {
7
+ return 'gzip';
8
+ }
9
+ }
@@ -0,0 +1,6 @@
1
+ import CompressorBase from './compressorBase';
2
+ export default abstract class StreamCompressor extends CompressorBase {
3
+ protected abstract get algorithm(): CompressionFormat;
4
+ compress(input: string): Promise<string>;
5
+ decompress(input: string): Promise<string>;
6
+ }
@@ -0,0 +1,36 @@
1
+ import CompressorBase from './compressorBase';
2
+ function ensureCompressionStream() {
3
+ if (!globalThis.CompressionStream || !globalThis.DecompressionStream) {
4
+ console.log('CompressionStream not supported. Loading polyfill.');
5
+ return import('../../../polyfill/streamPolyfill');
6
+ }
7
+ return Promise.resolve();
8
+ }
9
+ export default class StreamCompressor extends CompressorBase {
10
+ /* eslint-disable @typescript-eslint/no-floating-promises */
11
+ async compress(input) {
12
+ await ensureCompressionStream();
13
+ const blobToBase64 = (blob) => new Promise(resolve => {
14
+ const reader = new FileReader();
15
+ reader.onloadend = () => resolve(reader.result.split(',')[1]);
16
+ reader.readAsDataURL(blob);
17
+ });
18
+ const byteArray = new TextEncoder().encode(input);
19
+ const cs = new CompressionStream(this.algorithm);
20
+ const writer = cs.writable.getWriter();
21
+ writer.write(byteArray);
22
+ writer.close();
23
+ return new Response(cs.readable).blob().then(blobToBase64);
24
+ }
25
+ async decompress(input) {
26
+ await ensureCompressionStream();
27
+ const bytes = Uint8Array.from(atob(input), c => c.charCodeAt(0));
28
+ const cs = new DecompressionStream(this.algorithm);
29
+ const writer = cs.writable.getWriter();
30
+ writer.write(bytes);
31
+ writer.close();
32
+ return new Response(cs.readable).arrayBuffer().then(function (arrayBuffer) {
33
+ return new TextDecoder().decode(arrayBuffer);
34
+ });
35
+ }
36
+ }