@logic-pad/core 0.26.2 → 0.26.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/logic-core.global.d.ts +3264 -3264
- package/dist/benchmark/helper.d.ts +21 -0
- package/dist/benchmark/helper.js +34 -0
- package/dist/benchmark/prepareBench.d.ts +1 -0
- package/dist/benchmark/prepareBench.js +140 -0
- package/dist/benchmark/runBench.d.ts +1 -0
- package/dist/benchmark/runBench.js +206 -0
- package/dist/src/data/config.d.ts +119 -0
- package/dist/src/data/config.js +72 -0
- package/dist/src/data/configurable.d.ts +14 -0
- package/dist/src/data/configurable.js +26 -0
- package/dist/src/data/dataHelper.d.ts +92 -0
- package/dist/src/data/dataHelper.js +217 -0
- package/dist/src/data/events/eventHelper.d.ts +1 -0
- package/dist/src/data/events/eventHelper.js +6 -0
- package/dist/src/data/events/onFinalValidation.d.ts +14 -0
- package/dist/src/data/events/onFinalValidation.js +4 -0
- package/dist/src/data/events/onGetTile.d.ts +7 -0
- package/dist/src/data/events/onGetTile.js +4 -0
- package/dist/src/data/events/onGridChange.d.ts +6 -0
- package/dist/src/data/events/onGridChange.js +4 -0
- package/dist/src/data/events/onGridResize.d.ts +9 -0
- package/dist/src/data/events/onGridResize.js +4 -0
- package/dist/src/data/events/onSetGrid.d.ts +7 -0
- package/dist/src/data/events/onSetGrid.js +19 -0
- package/dist/src/data/events/onSymbolDisplay.d.ts +16 -0
- package/dist/src/data/events/onSymbolDisplay.js +4 -0
- package/dist/src/data/events/onSymbolMerge.d.ts +10 -0
- package/dist/src/data/events/onSymbolMerge.js +4 -0
- package/dist/src/data/events/onSymbolValidation.d.ts +18 -0
- package/dist/src/data/events/onSymbolValidation.js +4 -0
- package/dist/src/data/grid.d.ts +410 -0
- package/dist/src/data/grid.js +1106 -0
- package/dist/src/data/gridConnections.d.ts +25 -0
- package/dist/src/data/gridConnections.js +309 -0
- package/dist/src/data/gridZones.d.ts +26 -0
- package/dist/src/data/gridZones.js +117 -0
- package/dist/src/data/instruction.d.ts +26 -0
- package/dist/src/data/instruction.js +29 -0
- package/dist/src/data/primitives.d.ts +138 -0
- package/dist/src/data/primitives.js +177 -0
- package/dist/src/data/puzzle.d.ts +73 -0
- package/dist/src/data/puzzle.js +105 -0
- package/dist/src/data/rules/banPatternRule.d.ts +30 -0
- package/dist/src/data/rules/banPatternRule.js +125 -0
- package/dist/src/data/rules/cellCountPerZoneRule.d.ts +23 -0
- package/dist/src/data/rules/cellCountPerZoneRule.js +39 -0
- package/dist/src/data/rules/cellCountRule.d.ts +33 -0
- package/dist/src/data/rules/cellCountRule.js +138 -0
- package/dist/src/data/rules/completePatternRule.d.ts +24 -0
- package/dist/src/data/rules/completePatternRule.js +46 -0
- package/dist/src/data/rules/connectAllRule.d.ts +29 -0
- package/dist/src/data/rules/connectAllRule.js +88 -0
- package/dist/src/data/rules/connectZonesRule.d.ts +29 -0
- package/dist/src/data/rules/connectZonesRule.js +111 -0
- package/dist/src/data/rules/containsShapeRule.d.ts +34 -0
- package/dist/src/data/rules/containsShapeRule.js +125 -0
- package/dist/src/data/rules/customRule.d.ts +34 -0
- package/dist/src/data/rules/customRule.js +74 -0
- package/dist/src/data/rules/differentCountPerZoneRule.d.ts +30 -0
- package/dist/src/data/rules/differentCountPerZoneRule.js +96 -0
- package/dist/src/data/rules/exactCountPerZoneRule.d.ts +33 -0
- package/dist/src/data/rules/exactCountPerZoneRule.js +99 -0
- package/dist/src/data/rules/foresightRule.d.ts +36 -0
- package/dist/src/data/rules/foresightRule.js +107 -0
- package/dist/src/data/rules/index.d.ts +3 -0
- package/dist/src/data/rules/index.js +10 -0
- package/dist/src/data/rules/lyingSymbolRule.d.ts +31 -0
- package/dist/src/data/rules/lyingSymbolRule.js +207 -0
- package/dist/src/data/rules/musicControlLine.d.ts +82 -0
- package/dist/src/data/rules/musicControlLine.js +167 -0
- package/dist/src/data/rules/musicGridRule.d.ts +51 -0
- package/dist/src/data/rules/musicGridRule.js +212 -0
- package/dist/src/data/rules/mysteryRule.d.ts +39 -0
- package/dist/src/data/rules/mysteryRule.js +146 -0
- package/dist/src/data/rules/noLoopsRule.d.ts +29 -0
- package/dist/src/data/rules/noLoopsRule.js +218 -0
- package/dist/src/data/rules/offByXRule.d.ts +32 -0
- package/dist/src/data/rules/offByXRule.js +124 -0
- package/dist/src/data/rules/perfectionRule.d.ts +45 -0
- package/dist/src/data/rules/perfectionRule.js +158 -0
- package/dist/src/data/rules/regionAreaRule.d.ts +34 -0
- package/dist/src/data/rules/regionAreaRule.js +149 -0
- package/dist/src/data/rules/regionShapeRule.d.ts +22 -0
- package/dist/src/data/rules/regionShapeRule.js +58 -0
- package/dist/src/data/rules/rule.d.ts +18 -0
- package/dist/src/data/rules/rule.js +19 -0
- package/dist/src/data/rules/rules.gen.d.ts +23 -0
- package/dist/src/data/rules/rules.gen.js +27 -0
- package/dist/src/data/rules/sameCountPerZoneRule.d.ts +30 -0
- package/dist/src/data/rules/sameCountPerZoneRule.js +95 -0
- package/dist/src/data/rules/sameShapeRule.d.ts +28 -0
- package/dist/src/data/rules/sameShapeRule.js +68 -0
- package/dist/src/data/rules/symbolsPerRegionRule.d.ts +38 -0
- package/dist/src/data/rules/symbolsPerRegionRule.js +181 -0
- package/dist/src/data/rules/undercluedRule.d.ts +24 -0
- package/dist/src/data/rules/undercluedRule.js +53 -0
- package/dist/src/data/rules/uniqueShapeRule.d.ts +28 -0
- package/dist/src/data/rules/uniqueShapeRule.js +65 -0
- package/dist/src/data/rules/wrapAroundRule.d.ts +36 -0
- package/dist/src/data/rules/wrapAroundRule.js +241 -0
- package/dist/src/data/serializer/allSerializers.d.ts +35 -0
- package/dist/src/data/serializer/allSerializers.js +78 -0
- package/dist/src/data/serializer/compressor/allCompressors.d.ts +14 -0
- package/dist/src/data/serializer/compressor/allCompressors.js +43 -0
- package/dist/src/data/serializer/compressor/checksumCompressor.d.ts +6 -0
- package/dist/src/data/serializer/compressor/checksumCompressor.js +21 -0
- package/dist/src/data/serializer/compressor/compressorBase.d.ts +16 -0
- package/dist/src/data/serializer/compressor/compressorBase.js +2 -0
- package/dist/src/data/serializer/compressor/deflateCompressor.d.ts +7 -0
- package/dist/src/data/serializer/compressor/deflateCompressor.js +17 -0
- package/dist/src/data/serializer/compressor/gzipCompressor.d.ts +5 -0
- package/dist/src/data/serializer/compressor/gzipCompressor.js +9 -0
- package/dist/src/data/serializer/compressor/streamCompressor.d.ts +6 -0
- package/dist/src/data/serializer/compressor/streamCompressor.js +41 -0
- package/dist/src/data/serializer/serializerBase.d.ts +32 -0
- package/dist/src/data/serializer/serializerBase.js +2 -0
- package/dist/src/data/serializer/serializer_checksum.d.ts +35 -0
- package/dist/src/data/serializer/serializer_checksum.js +179 -0
- package/dist/src/data/serializer/serializer_v0.d.ts +55 -0
- package/dist/src/data/serializer/serializer_v0.js +484 -0
- package/dist/src/data/shapes.d.ts +19 -0
- package/dist/src/data/shapes.js +137 -0
- package/dist/src/data/solver/allSolvers.d.ts +3 -0
- package/dist/src/data/solver/allSolvers.js +13 -0
- package/dist/src/data/solver/auto/autoSolver.d.ts +18 -0
- package/dist/src/data/solver/auto/autoSolver.js +156 -0
- package/dist/src/data/solver/backtrack/backtrackSolver.d.ts +11 -0
- package/dist/src/data/solver/backtrack/backtrackSolver.js +54 -0
- package/dist/src/data/solver/backtrack/backtrackWorker.d.ts +1 -0
- package/dist/src/data/solver/backtrack/backtrackWorker.js +312 -0
- package/dist/src/data/solver/backtrack/data.d.ts +47 -0
- package/dist/src/data/solver/backtrack/data.js +151 -0
- package/dist/src/data/solver/backtrack/rules/banPattern.d.ts +9 -0
- package/dist/src/data/solver/backtrack/rules/banPattern.js +77 -0
- package/dist/src/data/solver/backtrack/rules/cellCount.d.ts +7 -0
- package/dist/src/data/solver/backtrack/rules/cellCount.js +25 -0
- package/dist/src/data/solver/backtrack/rules/connectAll.d.ts +7 -0
- package/dist/src/data/solver/backtrack/rules/connectAll.js +44 -0
- package/dist/src/data/solver/backtrack/rules/regionArea.d.ts +8 -0
- package/dist/src/data/solver/backtrack/rules/regionArea.js +71 -0
- package/dist/src/data/solver/backtrack/rules/regionShape.d.ts +8 -0
- package/dist/src/data/solver/backtrack/rules/regionShape.js +57 -0
- package/dist/src/data/solver/backtrack/rules/sameShape.d.ts +8 -0
- package/dist/src/data/solver/backtrack/rules/sameShape.js +14 -0
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.d.ts +10 -0
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.js +82 -0
- package/dist/src/data/solver/backtrack/rules/uniqueShape.d.ts +8 -0
- package/dist/src/data/solver/backtrack/rules/uniqueShape.js +14 -0
- package/dist/src/data/solver/backtrack/symbols/areaNumber.d.ts +9 -0
- package/dist/src/data/solver/backtrack/symbols/areaNumber.js +75 -0
- package/dist/src/data/solver/backtrack/symbols/dart.d.ts +8 -0
- package/dist/src/data/solver/backtrack/symbols/dart.js +45 -0
- package/dist/src/data/solver/backtrack/symbols/directionLinker.d.ts +11 -0
- package/dist/src/data/solver/backtrack/symbols/directionLinker.js +121 -0
- package/dist/src/data/solver/backtrack/symbols/focus.d.ts +9 -0
- package/dist/src/data/solver/backtrack/symbols/focus.js +48 -0
- package/dist/src/data/solver/backtrack/symbols/galaxy.d.ts +9 -0
- package/dist/src/data/solver/backtrack/symbols/galaxy.js +14 -0
- package/dist/src/data/solver/backtrack/symbols/letter.d.ts +9 -0
- package/dist/src/data/solver/backtrack/symbols/letter.js +95 -0
- package/dist/src/data/solver/backtrack/symbols/lotus.d.ts +11 -0
- package/dist/src/data/solver/backtrack/symbols/lotus.js +55 -0
- package/dist/src/data/solver/backtrack/symbols/minesweeper.d.ts +9 -0
- package/dist/src/data/solver/backtrack/symbols/minesweeper.js +44 -0
- package/dist/src/data/solver/backtrack/symbols/myopia.d.ts +7 -0
- package/dist/src/data/solver/backtrack/symbols/myopia.js +73 -0
- package/dist/src/data/solver/backtrack/symbols/viewpoint.d.ts +7 -0
- package/dist/src/data/solver/backtrack/symbols/viewpoint.js +51 -0
- package/dist/src/data/solver/cspuz/cspuzSolver.d.ts +13 -0
- package/dist/src/data/solver/cspuz/cspuzSolver.js +124 -0
- package/dist/src/data/solver/cspuz/cspuzWorker.d.ts +1 -0
- package/dist/src/data/solver/cspuz/cspuzWorker.js +82 -0
- package/dist/src/data/solver/cspuz/jsonify.d.ts +3 -0
- package/dist/src/data/solver/cspuz/jsonify.js +215 -0
- package/dist/src/data/solver/eventIteratingSolver.d.ts +8 -0
- package/dist/src/data/solver/eventIteratingSolver.js +54 -0
- package/dist/src/data/solver/solver.d.ts +77 -0
- package/dist/src/data/solver/solver.js +59 -0
- package/dist/src/data/solver/universal/universalSolver.d.ts +7 -0
- package/dist/src/data/solver/universal/universalSolver.js +13 -0
- package/dist/src/data/solver/universal/universalWorker.d.ts +1 -0
- package/dist/src/data/solver/universal/universalWorker.js +128 -0
- package/dist/src/data/symbols/areaNumberSymbol.d.ts +31 -0
- package/dist/src/data/symbols/areaNumberSymbol.js +80 -0
- package/dist/src/data/symbols/customIconSymbol.d.ts +35 -0
- package/dist/src/data/symbols/customIconSymbol.js +94 -0
- package/dist/src/data/symbols/customSymbol.d.ts +25 -0
- package/dist/src/data/symbols/customSymbol.js +45 -0
- package/dist/src/data/symbols/customTextSymbol.d.ts +35 -0
- package/dist/src/data/symbols/customTextSymbol.js +95 -0
- package/dist/src/data/symbols/dartSymbol.d.ts +36 -0
- package/dist/src/data/symbols/dartSymbol.js +96 -0
- package/dist/src/data/symbols/directionLinkerSymbol.d.ts +29 -0
- package/dist/src/data/symbols/directionLinkerSymbol.js +232 -0
- package/dist/src/data/symbols/everyLetterSymbol.d.ts +32 -0
- package/dist/src/data/symbols/everyLetterSymbol.js +119 -0
- package/dist/src/data/symbols/focusSymbol.d.ts +40 -0
- package/dist/src/data/symbols/focusSymbol.js +159 -0
- package/dist/src/data/symbols/galaxySymbol.d.ts +27 -0
- package/dist/src/data/symbols/galaxySymbol.js +61 -0
- package/dist/src/data/symbols/hiddenSymbol.d.ts +38 -0
- package/dist/src/data/symbols/hiddenSymbol.js +113 -0
- package/dist/src/data/symbols/houseSymbol.d.ts +33 -0
- package/dist/src/data/symbols/houseSymbol.js +104 -0
- package/dist/src/data/symbols/index.d.ts +3 -0
- package/dist/src/data/symbols/index.js +10 -0
- package/dist/src/data/symbols/letterSymbol.d.ts +32 -0
- package/dist/src/data/symbols/letterSymbol.js +118 -0
- package/dist/src/data/symbols/lotusSymbol.d.ts +30 -0
- package/dist/src/data/symbols/lotusSymbol.js +132 -0
- package/dist/src/data/symbols/minesweeperSymbol.d.ts +33 -0
- package/dist/src/data/symbols/minesweeperSymbol.js +106 -0
- package/dist/src/data/symbols/myopiaSymbol.d.ts +37 -0
- package/dist/src/data/symbols/myopiaSymbol.js +182 -0
- package/dist/src/data/symbols/numberSymbol.d.ts +19 -0
- package/dist/src/data/symbols/numberSymbol.js +32 -0
- package/dist/src/data/symbols/symbol.d.ts +29 -0
- package/dist/src/data/symbols/symbol.js +87 -0
- package/dist/src/data/symbols/symbols.gen.d.ts +15 -0
- package/dist/src/data/symbols/symbols.gen.js +19 -0
- package/dist/src/data/symbols/unsupportedSymbol.d.ts +23 -0
- package/dist/src/data/symbols/unsupportedSymbol.js +47 -0
- package/dist/src/data/symbols/viewpointSymbol.d.ts +32 -0
- package/dist/src/data/symbols/viewpointSymbol.js +95 -0
- package/dist/src/data/tile.d.ts +26 -0
- package/dist/src/data/tile.js +56 -0
- package/dist/src/data/tileConnections.d.ts +25 -0
- package/dist/src/data/tileConnections.js +74 -0
- package/dist/src/data/validate.d.ts +5 -0
- package/dist/src/data/validate.js +131 -0
- package/dist/src/data/validateAsync.d.ts +15 -0
- package/dist/src/data/validateAsync.js +71 -0
- package/dist/src/data/validateAsyncWorker.d.ts +1 -0
- package/dist/src/data/validateAsyncWorker.js +9 -0
- package/dist/src/index.d.ts +109 -0
- package/dist/src/index.js +112 -0
- package/dist/src/polyfill/streamPolyfill.d.ts +2 -0
- package/dist/src/polyfill/streamPolyfill.js +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { ConfigType } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { Color, State } from '../primitives.js';
|
|
4
|
+
import AreaNumberSymbol from '../symbols/areaNumberSymbol.js';
|
|
5
|
+
import Rule from './rule.js';
|
|
6
|
+
export default class CellCountRule extends Rule {
|
|
7
|
+
color;
|
|
8
|
+
count;
|
|
9
|
+
title = 'Total Count';
|
|
10
|
+
static CONFIGS = Object.freeze([
|
|
11
|
+
{
|
|
12
|
+
type: ConfigType.Color,
|
|
13
|
+
default: Color.Light,
|
|
14
|
+
allowGray: false,
|
|
15
|
+
field: 'color',
|
|
16
|
+
description: 'Color',
|
|
17
|
+
configurable: true,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: ConfigType.Number,
|
|
21
|
+
default: 10,
|
|
22
|
+
min: 0,
|
|
23
|
+
field: 'count',
|
|
24
|
+
description: 'Count',
|
|
25
|
+
configurable: true,
|
|
26
|
+
},
|
|
27
|
+
]);
|
|
28
|
+
static EXAMPLE_GRID_LIGHT = Object.freeze([
|
|
29
|
+
GridData.create(['bbbbb', 'bbbbb', 'bbbbb', 'bbbbb']),
|
|
30
|
+
GridData.create(['bbbbb', 'bbbbb', 'bwbbb', 'bbbbb']).withSymbols([
|
|
31
|
+
new AreaNumberSymbol(1, 2, 1),
|
|
32
|
+
]),
|
|
33
|
+
GridData.create(['bbbbb', 'bbbbb', 'bwwbb', 'bbbbb']).withSymbols([
|
|
34
|
+
new AreaNumberSymbol(1, 2, 2),
|
|
35
|
+
]),
|
|
36
|
+
GridData.create(['bbbbb', 'bwbbb', 'bwwbb', 'bbbbb']).withSymbols([
|
|
37
|
+
new AreaNumberSymbol(1, 2, 3),
|
|
38
|
+
]),
|
|
39
|
+
GridData.create(['bbbbb', 'bwwbb', 'bwwbb', 'bbbbb']).withSymbols([
|
|
40
|
+
new AreaNumberSymbol(1, 2, 4),
|
|
41
|
+
]),
|
|
42
|
+
GridData.create(['bbbbb', 'bwwbb', 'bwwwb', 'bbbbb']).withSymbols([
|
|
43
|
+
new AreaNumberSymbol(1, 2, 5),
|
|
44
|
+
]),
|
|
45
|
+
GridData.create(['bbbbb', 'bwwwb', 'bwwwb', 'bbbbb']).withSymbols([
|
|
46
|
+
new AreaNumberSymbol(1, 2, 6),
|
|
47
|
+
]),
|
|
48
|
+
GridData.create(['bbbbb', 'bbbbb', 'wwwbb', 'wwwwb']).withSymbols([
|
|
49
|
+
new AreaNumberSymbol(1, 2, 7),
|
|
50
|
+
]),
|
|
51
|
+
GridData.create(['bbbbb', 'wbbbb', 'wwwbb', 'wwwwb']).withSymbols([
|
|
52
|
+
new AreaNumberSymbol(1, 2, 8),
|
|
53
|
+
]),
|
|
54
|
+
GridData.create(['bbbbb', 'wwbbb', 'wwwbb', 'wwwwb']).withSymbols([
|
|
55
|
+
new AreaNumberSymbol(1, 2, 9),
|
|
56
|
+
]),
|
|
57
|
+
GridData.create(['wbbbb', 'wwbbb', 'wwwbb', 'wwwwb']).withSymbols([
|
|
58
|
+
new AreaNumberSymbol(1, 2, 10),
|
|
59
|
+
]),
|
|
60
|
+
]);
|
|
61
|
+
static EXAMPLE_GRID_DARK = Object.freeze(CellCountRule.EXAMPLE_GRID_LIGHT.map(grid => grid.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark))))));
|
|
62
|
+
static SEARCH_VARIANTS = [
|
|
63
|
+
new CellCountRule(Color.Light, 10).searchVariant(),
|
|
64
|
+
new CellCountRule(Color.Dark, 10).searchVariant(),
|
|
65
|
+
];
|
|
66
|
+
/**
|
|
67
|
+
* **There are <count> <color> cells in total**
|
|
68
|
+
*
|
|
69
|
+
* @param color - The color of the cells to count.
|
|
70
|
+
* @param count - The total number of cells of the given color.
|
|
71
|
+
*/
|
|
72
|
+
constructor(color, count) {
|
|
73
|
+
super();
|
|
74
|
+
this.color = color;
|
|
75
|
+
this.count = count;
|
|
76
|
+
this.color = color;
|
|
77
|
+
this.count = count;
|
|
78
|
+
}
|
|
79
|
+
get id() {
|
|
80
|
+
return `cell_count`;
|
|
81
|
+
}
|
|
82
|
+
get explanation() {
|
|
83
|
+
return `There ${this.count === 1 ? 'is' : 'are'} ${this.count} ${this.color} cell${this.count === 1 ? '' : 's'} *in total*`;
|
|
84
|
+
}
|
|
85
|
+
get configs() {
|
|
86
|
+
return CellCountRule.CONFIGS;
|
|
87
|
+
}
|
|
88
|
+
createExampleGrid() {
|
|
89
|
+
if (this.count < CellCountRule.EXAMPLE_GRID_LIGHT.length) {
|
|
90
|
+
if (this.color === Color.Light) {
|
|
91
|
+
return CellCountRule.EXAMPLE_GRID_LIGHT[this.count];
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
return CellCountRule.EXAMPLE_GRID_DARK[this.count];
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
const grid = this.color === Color.Light
|
|
99
|
+
? GridData.create(['wbbbb', 'wwbbb', 'wwwbb', 'wwwwb'])
|
|
100
|
+
: GridData.create(['bwwww', 'bbwww', 'bbbww', 'bbbbw']);
|
|
101
|
+
return grid.addSymbol(new AreaNumberSymbol(1, 2, this.count));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
get searchVariants() {
|
|
105
|
+
return CellCountRule.SEARCH_VARIANTS;
|
|
106
|
+
}
|
|
107
|
+
validateGrid(grid) {
|
|
108
|
+
let colored = 0;
|
|
109
|
+
let possible = 0;
|
|
110
|
+
grid.forEach(tile => {
|
|
111
|
+
if (!tile.exists)
|
|
112
|
+
return;
|
|
113
|
+
if (tile.color === this.color)
|
|
114
|
+
colored++;
|
|
115
|
+
if (tile.color === Color.Gray)
|
|
116
|
+
possible++;
|
|
117
|
+
});
|
|
118
|
+
if (colored > this.count || colored + possible < this.count) {
|
|
119
|
+
return { state: State.Error, positions: [] };
|
|
120
|
+
}
|
|
121
|
+
else if (colored === this.count && possible === 0) {
|
|
122
|
+
return { state: State.Satisfied };
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
return { state: State.Incomplete };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
copyWith({ color, count }) {
|
|
129
|
+
return new CellCountRule(color ?? this.color, count ?? this.count);
|
|
130
|
+
}
|
|
131
|
+
withColor(color) {
|
|
132
|
+
return this.copyWith({ color });
|
|
133
|
+
}
|
|
134
|
+
withCount(count) {
|
|
135
|
+
return this.copyWith({ count });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export const instance = new CellCountRule(Color.Dark, 10);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import GridData from '../grid.js';
|
|
2
|
+
import { RuleState } from '../primitives.js';
|
|
3
|
+
import Rule, { SearchVariant } from './rule.js';
|
|
4
|
+
export default class CompletePatternRule extends Rule {
|
|
5
|
+
readonly title = "Complete The Pattern";
|
|
6
|
+
get configExplanation(): string;
|
|
7
|
+
private static readonly EXAMPLE_GRID;
|
|
8
|
+
private static readonly SEARCH_VARIANTS;
|
|
9
|
+
/**
|
|
10
|
+
* **Complete the pattern**
|
|
11
|
+
*
|
|
12
|
+
* This rule validates answers based on the provided solution.
|
|
13
|
+
*/
|
|
14
|
+
constructor();
|
|
15
|
+
get id(): string;
|
|
16
|
+
get explanation(): string;
|
|
17
|
+
createExampleGrid(): GridData;
|
|
18
|
+
get searchVariants(): SearchVariant[];
|
|
19
|
+
validateGrid(_grid: GridData): RuleState;
|
|
20
|
+
copyWith(_: object): this;
|
|
21
|
+
get validateWithSolution(): boolean;
|
|
22
|
+
get isSingleton(): boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare const instance: CompletePatternRule;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import GridData from '../grid.js';
|
|
2
|
+
import { MajorRule, State } from '../primitives.js';
|
|
3
|
+
import Rule from './rule.js';
|
|
4
|
+
export default class CompletePatternRule extends Rule {
|
|
5
|
+
title = 'Complete The Pattern';
|
|
6
|
+
get configExplanation() {
|
|
7
|
+
return 'Complete the grid by pattern recognition. Your provided solution may override auto-validation.';
|
|
8
|
+
}
|
|
9
|
+
static EXAMPLE_GRID = Object.freeze(GridData.create(['wbwbw', 'bwbwb', 'wbwbw', 'bwbwb']));
|
|
10
|
+
static SEARCH_VARIANTS = [
|
|
11
|
+
new CompletePatternRule().searchVariant(),
|
|
12
|
+
];
|
|
13
|
+
/**
|
|
14
|
+
* **Complete the pattern**
|
|
15
|
+
*
|
|
16
|
+
* This rule validates answers based on the provided solution.
|
|
17
|
+
*/
|
|
18
|
+
constructor() {
|
|
19
|
+
super();
|
|
20
|
+
}
|
|
21
|
+
get id() {
|
|
22
|
+
return MajorRule.CompletePattern;
|
|
23
|
+
}
|
|
24
|
+
get explanation() {
|
|
25
|
+
return `Complete the pattern`;
|
|
26
|
+
}
|
|
27
|
+
createExampleGrid() {
|
|
28
|
+
return CompletePatternRule.EXAMPLE_GRID;
|
|
29
|
+
}
|
|
30
|
+
get searchVariants() {
|
|
31
|
+
return CompletePatternRule.SEARCH_VARIANTS;
|
|
32
|
+
}
|
|
33
|
+
validateGrid(_grid) {
|
|
34
|
+
return { state: State.Incomplete };
|
|
35
|
+
}
|
|
36
|
+
copyWith(_) {
|
|
37
|
+
return new CompletePatternRule();
|
|
38
|
+
}
|
|
39
|
+
get validateWithSolution() {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
get isSingleton() {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export const instance = new CompletePatternRule();
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AnyConfig } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { Color, RuleState } from '../primitives.js';
|
|
4
|
+
import Rule, { SearchVariant } from './rule.js';
|
|
5
|
+
export default class ConnectAllRule extends Rule {
|
|
6
|
+
readonly color: Color;
|
|
7
|
+
readonly title = "Connect All";
|
|
8
|
+
private static readonly CONFIGS;
|
|
9
|
+
private static readonly EXAMPLE_GRID_LIGHT;
|
|
10
|
+
private static readonly EXAMPLE_GRID_DARK;
|
|
11
|
+
private static readonly SEARCH_VARIANTS;
|
|
12
|
+
/**
|
|
13
|
+
* **Connect all <color> cells**
|
|
14
|
+
*
|
|
15
|
+
* @param color - The color of the cells to connect.
|
|
16
|
+
*/
|
|
17
|
+
constructor(color: Color);
|
|
18
|
+
get id(): string;
|
|
19
|
+
get explanation(): string;
|
|
20
|
+
get configs(): readonly AnyConfig[] | null;
|
|
21
|
+
createExampleGrid(): GridData;
|
|
22
|
+
get searchVariants(): SearchVariant[];
|
|
23
|
+
validateGrid(grid: GridData): RuleState;
|
|
24
|
+
copyWith({ color }: {
|
|
25
|
+
color?: Color;
|
|
26
|
+
}): this;
|
|
27
|
+
withColor(color: Color): this;
|
|
28
|
+
}
|
|
29
|
+
export declare const instance: ConnectAllRule;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { ConfigType } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { array, minBy } from '../dataHelper.js';
|
|
4
|
+
import { Color, State } from '../primitives.js';
|
|
5
|
+
import Rule from './rule.js';
|
|
6
|
+
export default class ConnectAllRule extends Rule {
|
|
7
|
+
color;
|
|
8
|
+
title = 'Connect All';
|
|
9
|
+
static CONFIGS = Object.freeze([
|
|
10
|
+
{
|
|
11
|
+
type: ConfigType.Color,
|
|
12
|
+
default: Color.Light,
|
|
13
|
+
allowGray: false,
|
|
14
|
+
field: 'color',
|
|
15
|
+
description: 'Color',
|
|
16
|
+
configurable: true,
|
|
17
|
+
},
|
|
18
|
+
]);
|
|
19
|
+
static EXAMPLE_GRID_LIGHT = Object.freeze(GridData.create(['bwwwb', 'bwbww', 'wwwbb', 'wbwww']));
|
|
20
|
+
static EXAMPLE_GRID_DARK = Object.freeze(GridData.create(['wbbbw', 'wbwbb', 'bbbww', 'bwbbb']));
|
|
21
|
+
static SEARCH_VARIANTS = [
|
|
22
|
+
new ConnectAllRule(Color.Light).searchVariant(),
|
|
23
|
+
new ConnectAllRule(Color.Dark).searchVariant(),
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* **Connect all <color> cells**
|
|
27
|
+
*
|
|
28
|
+
* @param color - The color of the cells to connect.
|
|
29
|
+
*/
|
|
30
|
+
constructor(color) {
|
|
31
|
+
super();
|
|
32
|
+
this.color = color;
|
|
33
|
+
this.color = color;
|
|
34
|
+
}
|
|
35
|
+
get id() {
|
|
36
|
+
return `connect_all`;
|
|
37
|
+
}
|
|
38
|
+
get explanation() {
|
|
39
|
+
return `Connect all ${this.color} cells`;
|
|
40
|
+
}
|
|
41
|
+
get configs() {
|
|
42
|
+
return ConnectAllRule.CONFIGS;
|
|
43
|
+
}
|
|
44
|
+
createExampleGrid() {
|
|
45
|
+
return this.color === Color.Light
|
|
46
|
+
? ConnectAllRule.EXAMPLE_GRID_LIGHT
|
|
47
|
+
: ConnectAllRule.EXAMPLE_GRID_DARK;
|
|
48
|
+
}
|
|
49
|
+
get searchVariants() {
|
|
50
|
+
return ConnectAllRule.SEARCH_VARIANTS;
|
|
51
|
+
}
|
|
52
|
+
validateGrid(grid) {
|
|
53
|
+
let complete = true;
|
|
54
|
+
const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
|
|
55
|
+
const islands = [];
|
|
56
|
+
while (true) {
|
|
57
|
+
const seed = grid.find((tile, x, y) => !visited[y][x] && tile.color === this.color);
|
|
58
|
+
if (!seed)
|
|
59
|
+
break;
|
|
60
|
+
const positions = [];
|
|
61
|
+
grid.iterateArea(seed, tile => tile.color === this.color || tile.color === Color.Gray, (tile, x, y) => {
|
|
62
|
+
if (tile.color === Color.Gray)
|
|
63
|
+
complete = false;
|
|
64
|
+
positions.push({ x, y });
|
|
65
|
+
}, visited);
|
|
66
|
+
islands.push(positions);
|
|
67
|
+
}
|
|
68
|
+
if (islands.length > 1) {
|
|
69
|
+
return {
|
|
70
|
+
state: State.Error,
|
|
71
|
+
positions: minBy(islands, island => island.length),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
else if (islands.length <= 1) {
|
|
75
|
+
return { state: complete ? State.Satisfied : State.Incomplete };
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
return { state: State.Incomplete }; // not reachable but the TS is not happy
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
copyWith({ color }) {
|
|
82
|
+
return new ConnectAllRule(color ?? this.color);
|
|
83
|
+
}
|
|
84
|
+
withColor(color) {
|
|
85
|
+
return this.copyWith({ color });
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export const instance = new ConnectAllRule(Color.Dark);
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { AnyConfig } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { Color, RuleState } from '../primitives.js';
|
|
4
|
+
import Rule, { SearchVariant } from './rule.js';
|
|
5
|
+
export default class ConnectZonesRule extends Rule {
|
|
6
|
+
readonly color: Color;
|
|
7
|
+
readonly title = "Connect Zones";
|
|
8
|
+
private static readonly CONFIGS;
|
|
9
|
+
private static readonly EXAMPLE_GRID_LIGHT;
|
|
10
|
+
private static readonly EXAMPLE_GRID_DARK;
|
|
11
|
+
private static readonly SEARCH_VARIANTS;
|
|
12
|
+
/**
|
|
13
|
+
* **Connect all <color> cells in each zone**
|
|
14
|
+
*
|
|
15
|
+
* @param color - The color of the cells to connect.
|
|
16
|
+
*/
|
|
17
|
+
constructor(color: Color);
|
|
18
|
+
get id(): string;
|
|
19
|
+
get explanation(): string;
|
|
20
|
+
get configs(): readonly AnyConfig[] | null;
|
|
21
|
+
createExampleGrid(): GridData;
|
|
22
|
+
get searchVariants(): SearchVariant[];
|
|
23
|
+
validateGrid(grid: GridData): RuleState;
|
|
24
|
+
copyWith({ color }: {
|
|
25
|
+
color?: Color;
|
|
26
|
+
}): this;
|
|
27
|
+
withColor(color: Color): this;
|
|
28
|
+
}
|
|
29
|
+
export declare const instance: ConnectZonesRule;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { ConfigType } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { array, minBy } from '../dataHelper.js';
|
|
4
|
+
import { Color, State } from '../primitives.js';
|
|
5
|
+
import Rule from './rule.js';
|
|
6
|
+
import GridZones from '../gridZones.js';
|
|
7
|
+
export default class ConnectZonesRule extends Rule {
|
|
8
|
+
color;
|
|
9
|
+
title = 'Connect Zones';
|
|
10
|
+
static CONFIGS = Object.freeze([
|
|
11
|
+
{
|
|
12
|
+
type: ConfigType.Color,
|
|
13
|
+
default: Color.Light,
|
|
14
|
+
allowGray: false,
|
|
15
|
+
field: 'color',
|
|
16
|
+
description: 'Color',
|
|
17
|
+
configurable: true,
|
|
18
|
+
},
|
|
19
|
+
]);
|
|
20
|
+
static EXAMPLE_GRID_LIGHT = Object.freeze(GridData.create(['wbbwb', 'wbbwb', 'wbbww', 'wwbbb'])
|
|
21
|
+
.withZones(new GridZones([
|
|
22
|
+
{ x1: 2, y1: 1, x2: 2, y2: 2 },
|
|
23
|
+
{ x1: 1, y1: 0, x2: 2, y2: 0 },
|
|
24
|
+
{ x1: 1, y1: 1, x2: 2, y2: 1 },
|
|
25
|
+
{ x1: 2, y1: 2, x2: 3, y2: 2 },
|
|
26
|
+
{ x1: 2, y1: 3, x2: 3, y2: 3 },
|
|
27
|
+
]))
|
|
28
|
+
.addRule(new ConnectZonesRule(Color.Light)));
|
|
29
|
+
static EXAMPLE_GRID_DARK = Object.freeze(ConnectZonesRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))));
|
|
30
|
+
static SEARCH_VARIANTS = [
|
|
31
|
+
new ConnectZonesRule(Color.Light).searchVariant(),
|
|
32
|
+
new ConnectZonesRule(Color.Dark).searchVariant(),
|
|
33
|
+
];
|
|
34
|
+
/**
|
|
35
|
+
* **Connect all <color> cells in each zone**
|
|
36
|
+
*
|
|
37
|
+
* @param color - The color of the cells to connect.
|
|
38
|
+
*/
|
|
39
|
+
constructor(color) {
|
|
40
|
+
super();
|
|
41
|
+
this.color = color;
|
|
42
|
+
this.color = color;
|
|
43
|
+
}
|
|
44
|
+
get id() {
|
|
45
|
+
return `connect_zones`;
|
|
46
|
+
}
|
|
47
|
+
get explanation() {
|
|
48
|
+
return `Connect all ${this.color} cells in each zone`;
|
|
49
|
+
}
|
|
50
|
+
get configs() {
|
|
51
|
+
return ConnectZonesRule.CONFIGS;
|
|
52
|
+
}
|
|
53
|
+
createExampleGrid() {
|
|
54
|
+
return this.color === Color.Light
|
|
55
|
+
? ConnectZonesRule.EXAMPLE_GRID_LIGHT
|
|
56
|
+
: ConnectZonesRule.EXAMPLE_GRID_DARK;
|
|
57
|
+
}
|
|
58
|
+
get searchVariants() {
|
|
59
|
+
return ConnectZonesRule.SEARCH_VARIANTS;
|
|
60
|
+
}
|
|
61
|
+
validateGrid(grid) {
|
|
62
|
+
let complete = true;
|
|
63
|
+
let zoneId = 1;
|
|
64
|
+
const zoneMap = array(grid.width, grid.height, () => 0);
|
|
65
|
+
const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
|
|
66
|
+
const zones = grid.reduceByZone((zone, tile, x, y) => {
|
|
67
|
+
zoneMap[y][x] = zone;
|
|
68
|
+
if (tile.exists && tile.color === Color.Gray) {
|
|
69
|
+
complete = false;
|
|
70
|
+
}
|
|
71
|
+
return zone;
|
|
72
|
+
}, () => zoneId++);
|
|
73
|
+
for (const zone of zones) {
|
|
74
|
+
const islands = [];
|
|
75
|
+
while (true) {
|
|
76
|
+
const seed = grid.find((tile, x, y) => !visited[y][x] &&
|
|
77
|
+
zoneMap[y][x] === zone &&
|
|
78
|
+
tile.color === this.color);
|
|
79
|
+
if (!seed)
|
|
80
|
+
break;
|
|
81
|
+
const positions = [];
|
|
82
|
+
grid.iterateArea(seed, (tile, x, y) => {
|
|
83
|
+
if (tile.color !== this.color && tile.color !== Color.Gray) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
const pos = grid.toArrayCoordinates(x, y);
|
|
87
|
+
return zoneMap[pos.y][pos.x] === zone;
|
|
88
|
+
}, (tile, x, y) => {
|
|
89
|
+
if (tile.color === Color.Gray)
|
|
90
|
+
complete = false;
|
|
91
|
+
positions.push({ x, y });
|
|
92
|
+
}, visited);
|
|
93
|
+
islands.push(positions);
|
|
94
|
+
}
|
|
95
|
+
if (islands.length > 1) {
|
|
96
|
+
return {
|
|
97
|
+
state: State.Error,
|
|
98
|
+
positions: minBy(islands, island => island.length),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { state: complete ? State.Satisfied : State.Incomplete };
|
|
103
|
+
}
|
|
104
|
+
copyWith({ color }) {
|
|
105
|
+
return new ConnectZonesRule(color ?? this.color);
|
|
106
|
+
}
|
|
107
|
+
withColor(color) {
|
|
108
|
+
return this.copyWith({ color });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export const instance = new ConnectZonesRule(Color.Dark);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AnyConfig } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { Color, RuleState } from '../primitives.js';
|
|
4
|
+
import { Shape } from '../shapes.js';
|
|
5
|
+
import RegionShapeRule from './regionShapeRule.js';
|
|
6
|
+
import { SearchVariant } from './rule.js';
|
|
7
|
+
export default class ContainsShapeRule extends RegionShapeRule {
|
|
8
|
+
readonly title = "Areas Contain Pattern";
|
|
9
|
+
private static readonly EXAMPLE_GRID_LIGHT;
|
|
10
|
+
private static readonly EXAMPLE_GRID_DARK;
|
|
11
|
+
private static readonly CONFIGS;
|
|
12
|
+
private static readonly SEARCH_VARIANTS;
|
|
13
|
+
readonly pattern: GridData;
|
|
14
|
+
readonly cache: Shape[];
|
|
15
|
+
/**
|
|
16
|
+
* **All <color> areas must contain this pattern**
|
|
17
|
+
*
|
|
18
|
+
* @param color - The color of the regions to compare.
|
|
19
|
+
* @param pattern - GridData representing the required pattern. Only non-gray tiles are considered.
|
|
20
|
+
*/
|
|
21
|
+
constructor(color: Color, pattern: GridData);
|
|
22
|
+
get id(): string;
|
|
23
|
+
get explanation(): string;
|
|
24
|
+
get configs(): readonly AnyConfig[] | null;
|
|
25
|
+
createExampleGrid(): GridData;
|
|
26
|
+
get searchVariants(): SearchVariant[];
|
|
27
|
+
validateGrid(grid: GridData): RuleState;
|
|
28
|
+
copyWith({ color, pattern, }: {
|
|
29
|
+
color?: Color;
|
|
30
|
+
pattern?: GridData;
|
|
31
|
+
}): this;
|
|
32
|
+
withPattern(pattern: GridData): this;
|
|
33
|
+
}
|
|
34
|
+
export declare const instance: ContainsShapeRule;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ConfigType } from '../config.js';
|
|
2
|
+
import { array } from '../dataHelper.js';
|
|
3
|
+
import GridData from '../grid.js';
|
|
4
|
+
import { Color, State } from '../primitives.js';
|
|
5
|
+
import { getShapeVariants, sanitizePatternGrid, tilesToShape, } from '../shapes.js';
|
|
6
|
+
import RegionShapeRule from './regionShapeRule.js';
|
|
7
|
+
export default class ContainsShapeRule extends RegionShapeRule {
|
|
8
|
+
title = 'Areas Contain Pattern';
|
|
9
|
+
static EXAMPLE_GRID_LIGHT = Object.freeze(GridData.create(['nnnnn', 'nnnnn', 'wwwwn', 'nnnnn', 'nnnnn']));
|
|
10
|
+
static EXAMPLE_GRID_DARK = Object.freeze(GridData.create(['nnnnn', 'nnnnn', 'bbbbn', 'nnnnn', 'nnnnn']));
|
|
11
|
+
static CONFIGS = Object.freeze([
|
|
12
|
+
{
|
|
13
|
+
type: ConfigType.Color,
|
|
14
|
+
default: Color.Light,
|
|
15
|
+
allowGray: false,
|
|
16
|
+
field: 'color',
|
|
17
|
+
description: 'Color',
|
|
18
|
+
configurable: true,
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
type: ConfigType.Shape,
|
|
22
|
+
default: ContainsShapeRule.EXAMPLE_GRID_LIGHT,
|
|
23
|
+
resizable: true,
|
|
24
|
+
field: 'pattern',
|
|
25
|
+
description: 'Pattern',
|
|
26
|
+
explanation: 'The pattern to be contained. Must only include tiles of the selected color.',
|
|
27
|
+
configurable: true,
|
|
28
|
+
},
|
|
29
|
+
]);
|
|
30
|
+
static SEARCH_VARIANTS = [
|
|
31
|
+
new ContainsShapeRule(Color.Light, ContainsShapeRule.EXAMPLE_GRID_LIGHT).searchVariant(),
|
|
32
|
+
new ContainsShapeRule(Color.Dark, ContainsShapeRule.EXAMPLE_GRID_DARK).searchVariant(),
|
|
33
|
+
];
|
|
34
|
+
pattern;
|
|
35
|
+
cache;
|
|
36
|
+
/**
|
|
37
|
+
* **All <color> areas must contain this pattern**
|
|
38
|
+
*
|
|
39
|
+
* @param color - The color of the regions to compare.
|
|
40
|
+
* @param pattern - GridData representing the required pattern. Only non-gray tiles are considered.
|
|
41
|
+
*/
|
|
42
|
+
constructor(color, pattern) {
|
|
43
|
+
super(color);
|
|
44
|
+
this.pattern = sanitizePatternGrid(pattern, t => t.color === color ? t : t.withColor(Color.Gray));
|
|
45
|
+
this.cache = getShapeVariants(tilesToShape(this.pattern.tiles));
|
|
46
|
+
}
|
|
47
|
+
get id() {
|
|
48
|
+
return `contains_shape`;
|
|
49
|
+
}
|
|
50
|
+
get explanation() {
|
|
51
|
+
return `All ${this.color} areas must contain this pattern`;
|
|
52
|
+
}
|
|
53
|
+
get configs() {
|
|
54
|
+
return ContainsShapeRule.CONFIGS;
|
|
55
|
+
}
|
|
56
|
+
createExampleGrid() {
|
|
57
|
+
let minX = Number.POSITIVE_INFINITY;
|
|
58
|
+
let minY = Number.POSITIVE_INFINITY;
|
|
59
|
+
let maxX = Number.NEGATIVE_INFINITY;
|
|
60
|
+
let maxY = Number.NEGATIVE_INFINITY;
|
|
61
|
+
this.pattern.forEach((tile, x, y) => {
|
|
62
|
+
if (tile.color !== Color.Gray && tile.exists) {
|
|
63
|
+
minX = Math.min(minX, x);
|
|
64
|
+
minY = Math.min(minY, y);
|
|
65
|
+
maxX = Math.max(maxX, x);
|
|
66
|
+
maxY = Math.max(maxY, y);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
const width = maxX - minX + 1;
|
|
70
|
+
const height = maxY - minY + 1;
|
|
71
|
+
if (!Number.isFinite(width) || !Number.isFinite(height)) {
|
|
72
|
+
return GridData.create(0, 0);
|
|
73
|
+
}
|
|
74
|
+
const tiles = array(width, height, (x, y) => {
|
|
75
|
+
const tile = this.pattern.getTile(x + minX, y + minY);
|
|
76
|
+
if (!tile.exists || tile.color !== Color.Gray)
|
|
77
|
+
return tile;
|
|
78
|
+
return tile.withExists(false);
|
|
79
|
+
});
|
|
80
|
+
return GridData.create(width, height, tiles);
|
|
81
|
+
}
|
|
82
|
+
get searchVariants() {
|
|
83
|
+
return ContainsShapeRule.SEARCH_VARIANTS;
|
|
84
|
+
}
|
|
85
|
+
validateGrid(grid) {
|
|
86
|
+
const { regions, complete } = this.getShapeRegions(grid);
|
|
87
|
+
const errorRegion = regions.find(r => {
|
|
88
|
+
for (const pattern of this.cache) {
|
|
89
|
+
if (r.shape.elements.length < pattern.elements.length)
|
|
90
|
+
continue;
|
|
91
|
+
for (let y = 0; y <= r.shape.height - pattern.height; y++) {
|
|
92
|
+
for (let x = 0; x <= r.shape.width - pattern.width; x++) {
|
|
93
|
+
let match = true;
|
|
94
|
+
for (const element of pattern.elements) {
|
|
95
|
+
const tile = r.shape.elements.find(e => e.x === x + element.x && e.y === y + element.y);
|
|
96
|
+
if (!tile || tile.color !== element.color) {
|
|
97
|
+
match = false;
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (match)
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
if (errorRegion) {
|
|
109
|
+
return {
|
|
110
|
+
state: State.Error,
|
|
111
|
+
positions: errorRegion.positions,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
return { state: complete ? State.Satisfied : State.Incomplete };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
copyWith({ color, pattern, }) {
|
|
119
|
+
return new ContainsShapeRule(color ?? this.color, pattern ?? this.pattern);
|
|
120
|
+
}
|
|
121
|
+
withPattern(pattern) {
|
|
122
|
+
return this.copyWith({ pattern });
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
export const instance = new ContainsShapeRule(Color.Dark, GridData.create([]));
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AnyConfig } from '../config.js';
|
|
2
|
+
import GridData from '../grid.js';
|
|
3
|
+
import { RuleState } from '../primitives.js';
|
|
4
|
+
import Rule, { SearchVariant } from './rule.js';
|
|
5
|
+
export default class CustomRule extends Rule {
|
|
6
|
+
readonly description: string;
|
|
7
|
+
readonly grid: GridData;
|
|
8
|
+
readonly title = "Custom Rule";
|
|
9
|
+
get configExplanation(): string;
|
|
10
|
+
private static readonly EXAMPLE_GRID;
|
|
11
|
+
static readonly configs: readonly AnyConfig[];
|
|
12
|
+
private static readonly SEARCH_VARIANTS;
|
|
13
|
+
/**
|
|
14
|
+
* A custom rule with a description and thumbnail grid.
|
|
15
|
+
*
|
|
16
|
+
* This rule validates answers based on the provided solution.
|
|
17
|
+
*
|
|
18
|
+
* @param description - The description of the rule.
|
|
19
|
+
* @param grid - The thumbnail grid of the rule, preferably 5x4 in size.
|
|
20
|
+
*/
|
|
21
|
+
constructor(description: string, grid: GridData);
|
|
22
|
+
get id(): string;
|
|
23
|
+
get explanation(): string;
|
|
24
|
+
get configs(): readonly AnyConfig[] | null;
|
|
25
|
+
createExampleGrid(): GridData;
|
|
26
|
+
get searchVariants(): SearchVariant[];
|
|
27
|
+
validateGrid(_grid: GridData): RuleState;
|
|
28
|
+
copyWith({ description, grid, }: {
|
|
29
|
+
description?: string;
|
|
30
|
+
grid?: GridData;
|
|
31
|
+
}): this;
|
|
32
|
+
get validateWithSolution(): boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare const instance: CustomRule;
|