@logic-pad/core 0.26.1 → 0.26.2
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/README.md +31 -31
- package/assets/logic-core.global.d.ts +3264 -3262
- package/package.json +4 -1
- package/dist/benchmark/helper.d.ts +0 -21
- package/dist/benchmark/helper.js +0 -34
- package/dist/benchmark/prepareBench.d.ts +0 -1
- package/dist/benchmark/prepareBench.js +0 -140
- package/dist/benchmark/runBench.d.ts +0 -1
- package/dist/benchmark/runBench.js +0 -206
- package/dist/src/data/config.d.ts +0 -119
- package/dist/src/data/config.js +0 -72
- package/dist/src/data/configurable.d.ts +0 -14
- package/dist/src/data/configurable.js +0 -26
- package/dist/src/data/dataHelper.d.ts +0 -92
- package/dist/src/data/dataHelper.js +0 -217
- package/dist/src/data/events/eventHelper.d.ts +0 -1
- package/dist/src/data/events/eventHelper.js +0 -6
- package/dist/src/data/events/onFinalValidation.d.ts +0 -14
- package/dist/src/data/events/onFinalValidation.js +0 -4
- package/dist/src/data/events/onGetTile.d.ts +0 -7
- package/dist/src/data/events/onGetTile.js +0 -4
- package/dist/src/data/events/onGridChange.d.ts +0 -6
- package/dist/src/data/events/onGridChange.js +0 -4
- package/dist/src/data/events/onGridResize.d.ts +0 -9
- package/dist/src/data/events/onGridResize.js +0 -4
- package/dist/src/data/events/onSetGrid.d.ts +0 -7
- package/dist/src/data/events/onSetGrid.js +0 -19
- package/dist/src/data/events/onSymbolDisplay.d.ts +0 -16
- package/dist/src/data/events/onSymbolDisplay.js +0 -4
- package/dist/src/data/events/onSymbolMerge.d.ts +0 -10
- package/dist/src/data/events/onSymbolMerge.js +0 -4
- package/dist/src/data/events/onSymbolValidation.d.ts +0 -18
- package/dist/src/data/events/onSymbolValidation.js +0 -4
- package/dist/src/data/grid.d.ts +0 -410
- package/dist/src/data/grid.js +0 -1106
- package/dist/src/data/gridConnections.d.ts +0 -25
- package/dist/src/data/gridConnections.js +0 -309
- package/dist/src/data/gridZones.d.ts +0 -26
- package/dist/src/data/gridZones.js +0 -117
- package/dist/src/data/instruction.d.ts +0 -26
- package/dist/src/data/instruction.js +0 -29
- package/dist/src/data/primitives.d.ts +0 -138
- package/dist/src/data/primitives.js +0 -177
- package/dist/src/data/puzzle.d.ts +0 -73
- package/dist/src/data/puzzle.js +0 -105
- package/dist/src/data/rules/banPatternRule.d.ts +0 -30
- package/dist/src/data/rules/banPatternRule.js +0 -125
- package/dist/src/data/rules/cellCountPerZoneRule.d.ts +0 -23
- package/dist/src/data/rules/cellCountPerZoneRule.js +0 -39
- package/dist/src/data/rules/cellCountRule.d.ts +0 -33
- package/dist/src/data/rules/cellCountRule.js +0 -138
- package/dist/src/data/rules/completePatternRule.d.ts +0 -24
- package/dist/src/data/rules/completePatternRule.js +0 -46
- package/dist/src/data/rules/connectAllRule.d.ts +0 -29
- package/dist/src/data/rules/connectAllRule.js +0 -88
- package/dist/src/data/rules/connectZonesRule.d.ts +0 -29
- package/dist/src/data/rules/connectZonesRule.js +0 -111
- package/dist/src/data/rules/containsShapeRule.d.ts +0 -34
- package/dist/src/data/rules/containsShapeRule.js +0 -125
- package/dist/src/data/rules/customRule.d.ts +0 -34
- package/dist/src/data/rules/customRule.js +0 -74
- package/dist/src/data/rules/differentCountPerZoneRule.d.ts +0 -30
- package/dist/src/data/rules/differentCountPerZoneRule.js +0 -96
- package/dist/src/data/rules/exactCountPerZoneRule.d.ts +0 -33
- package/dist/src/data/rules/exactCountPerZoneRule.js +0 -99
- package/dist/src/data/rules/foresightRule.d.ts +0 -36
- package/dist/src/data/rules/foresightRule.js +0 -107
- package/dist/src/data/rules/index.d.ts +0 -3
- package/dist/src/data/rules/index.js +0 -10
- package/dist/src/data/rules/lyingSymbolRule.d.ts +0 -31
- package/dist/src/data/rules/lyingSymbolRule.js +0 -207
- package/dist/src/data/rules/musicControlLine.d.ts +0 -82
- package/dist/src/data/rules/musicControlLine.js +0 -167
- package/dist/src/data/rules/musicGridRule.d.ts +0 -51
- package/dist/src/data/rules/musicGridRule.js +0 -212
- package/dist/src/data/rules/mysteryRule.d.ts +0 -39
- package/dist/src/data/rules/mysteryRule.js +0 -146
- package/dist/src/data/rules/noLoopsRule.d.ts +0 -29
- package/dist/src/data/rules/noLoopsRule.js +0 -218
- package/dist/src/data/rules/offByXRule.d.ts +0 -32
- package/dist/src/data/rules/offByXRule.js +0 -124
- package/dist/src/data/rules/perfectionRule.d.ts +0 -45
- package/dist/src/data/rules/perfectionRule.js +0 -158
- package/dist/src/data/rules/regionAreaRule.d.ts +0 -34
- package/dist/src/data/rules/regionAreaRule.js +0 -149
- package/dist/src/data/rules/regionShapeRule.d.ts +0 -22
- package/dist/src/data/rules/regionShapeRule.js +0 -58
- package/dist/src/data/rules/rule.d.ts +0 -18
- package/dist/src/data/rules/rule.js +0 -19
- package/dist/src/data/rules/rules.gen.d.ts +0 -23
- package/dist/src/data/rules/rules.gen.js +0 -27
- package/dist/src/data/rules/sameCountPerZoneRule.d.ts +0 -30
- package/dist/src/data/rules/sameCountPerZoneRule.js +0 -95
- package/dist/src/data/rules/sameShapeRule.d.ts +0 -28
- package/dist/src/data/rules/sameShapeRule.js +0 -68
- package/dist/src/data/rules/symbolsPerRegionRule.d.ts +0 -38
- package/dist/src/data/rules/symbolsPerRegionRule.js +0 -181
- package/dist/src/data/rules/undercluedRule.d.ts +0 -24
- package/dist/src/data/rules/undercluedRule.js +0 -53
- package/dist/src/data/rules/uniqueShapeRule.d.ts +0 -28
- package/dist/src/data/rules/uniqueShapeRule.js +0 -65
- package/dist/src/data/rules/wrapAroundRule.d.ts +0 -36
- package/dist/src/data/rules/wrapAroundRule.js +0 -241
- package/dist/src/data/serializer/allSerializers.d.ts +0 -32
- package/dist/src/data/serializer/allSerializers.js +0 -71
- package/dist/src/data/serializer/compressor/allCompressors.d.ts +0 -14
- package/dist/src/data/serializer/compressor/allCompressors.js +0 -43
- package/dist/src/data/serializer/compressor/checksumCompressor.d.ts +0 -6
- package/dist/src/data/serializer/compressor/checksumCompressor.js +0 -21
- package/dist/src/data/serializer/compressor/compressorBase.d.ts +0 -16
- package/dist/src/data/serializer/compressor/compressorBase.js +0 -2
- package/dist/src/data/serializer/compressor/deflateCompressor.d.ts +0 -7
- package/dist/src/data/serializer/compressor/deflateCompressor.js +0 -17
- package/dist/src/data/serializer/compressor/gzipCompressor.d.ts +0 -5
- package/dist/src/data/serializer/compressor/gzipCompressor.js +0 -9
- package/dist/src/data/serializer/compressor/streamCompressor.d.ts +0 -6
- package/dist/src/data/serializer/compressor/streamCompressor.js +0 -41
- package/dist/src/data/serializer/serializerBase.d.ts +0 -32
- package/dist/src/data/serializer/serializerBase.js +0 -2
- package/dist/src/data/serializer/serializer_checksum.d.ts +0 -35
- package/dist/src/data/serializer/serializer_checksum.js +0 -179
- package/dist/src/data/serializer/serializer_v0.d.ts +0 -55
- package/dist/src/data/serializer/serializer_v0.js +0 -484
- package/dist/src/data/shapes.d.ts +0 -19
- package/dist/src/data/shapes.js +0 -137
- package/dist/src/data/solver/allSolvers.d.ts +0 -3
- package/dist/src/data/solver/allSolvers.js +0 -13
- package/dist/src/data/solver/auto/autoSolver.d.ts +0 -18
- package/dist/src/data/solver/auto/autoSolver.js +0 -156
- package/dist/src/data/solver/backtrack/backtrackSolver.d.ts +0 -11
- package/dist/src/data/solver/backtrack/backtrackSolver.js +0 -54
- package/dist/src/data/solver/backtrack/backtrackWorker.d.ts +0 -1
- package/dist/src/data/solver/backtrack/backtrackWorker.js +0 -312
- package/dist/src/data/solver/backtrack/data.d.ts +0 -47
- package/dist/src/data/solver/backtrack/data.js +0 -151
- package/dist/src/data/solver/backtrack/rules/banPattern.d.ts +0 -9
- package/dist/src/data/solver/backtrack/rules/banPattern.js +0 -77
- package/dist/src/data/solver/backtrack/rules/cellCount.d.ts +0 -7
- package/dist/src/data/solver/backtrack/rules/cellCount.js +0 -25
- package/dist/src/data/solver/backtrack/rules/connectAll.d.ts +0 -7
- package/dist/src/data/solver/backtrack/rules/connectAll.js +0 -44
- package/dist/src/data/solver/backtrack/rules/regionArea.d.ts +0 -8
- package/dist/src/data/solver/backtrack/rules/regionArea.js +0 -71
- package/dist/src/data/solver/backtrack/rules/regionShape.d.ts +0 -8
- package/dist/src/data/solver/backtrack/rules/regionShape.js +0 -57
- package/dist/src/data/solver/backtrack/rules/sameShape.d.ts +0 -8
- package/dist/src/data/solver/backtrack/rules/sameShape.js +0 -14
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.d.ts +0 -10
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.js +0 -82
- package/dist/src/data/solver/backtrack/rules/uniqueShape.d.ts +0 -8
- package/dist/src/data/solver/backtrack/rules/uniqueShape.js +0 -14
- package/dist/src/data/solver/backtrack/symbols/areaNumber.d.ts +0 -9
- package/dist/src/data/solver/backtrack/symbols/areaNumber.js +0 -75
- package/dist/src/data/solver/backtrack/symbols/dart.d.ts +0 -8
- package/dist/src/data/solver/backtrack/symbols/dart.js +0 -45
- package/dist/src/data/solver/backtrack/symbols/directionLinker.d.ts +0 -11
- package/dist/src/data/solver/backtrack/symbols/directionLinker.js +0 -121
- package/dist/src/data/solver/backtrack/symbols/focus.d.ts +0 -9
- package/dist/src/data/solver/backtrack/symbols/focus.js +0 -48
- package/dist/src/data/solver/backtrack/symbols/galaxy.d.ts +0 -9
- package/dist/src/data/solver/backtrack/symbols/galaxy.js +0 -14
- package/dist/src/data/solver/backtrack/symbols/letter.d.ts +0 -9
- package/dist/src/data/solver/backtrack/symbols/letter.js +0 -95
- package/dist/src/data/solver/backtrack/symbols/lotus.d.ts +0 -11
- package/dist/src/data/solver/backtrack/symbols/lotus.js +0 -55
- package/dist/src/data/solver/backtrack/symbols/minesweeper.d.ts +0 -9
- package/dist/src/data/solver/backtrack/symbols/minesweeper.js +0 -44
- package/dist/src/data/solver/backtrack/symbols/myopia.d.ts +0 -7
- package/dist/src/data/solver/backtrack/symbols/myopia.js +0 -73
- package/dist/src/data/solver/backtrack/symbols/viewpoint.d.ts +0 -7
- package/dist/src/data/solver/backtrack/symbols/viewpoint.js +0 -51
- package/dist/src/data/solver/cspuz/cspuzSolver.d.ts +0 -13
- package/dist/src/data/solver/cspuz/cspuzSolver.js +0 -124
- package/dist/src/data/solver/cspuz/cspuzWorker.d.ts +0 -1
- package/dist/src/data/solver/cspuz/cspuzWorker.js +0 -82
- package/dist/src/data/solver/cspuz/jsonify.d.ts +0 -3
- package/dist/src/data/solver/cspuz/jsonify.js +0 -215
- package/dist/src/data/solver/eventIteratingSolver.d.ts +0 -8
- package/dist/src/data/solver/eventIteratingSolver.js +0 -54
- package/dist/src/data/solver/solver.d.ts +0 -77
- package/dist/src/data/solver/solver.js +0 -59
- package/dist/src/data/solver/universal/universalSolver.d.ts +0 -7
- package/dist/src/data/solver/universal/universalSolver.js +0 -13
- package/dist/src/data/solver/universal/universalWorker.d.ts +0 -1
- package/dist/src/data/solver/universal/universalWorker.js +0 -128
- package/dist/src/data/symbols/areaNumberSymbol.d.ts +0 -31
- package/dist/src/data/symbols/areaNumberSymbol.js +0 -80
- package/dist/src/data/symbols/customIconSymbol.d.ts +0 -35
- package/dist/src/data/symbols/customIconSymbol.js +0 -94
- package/dist/src/data/symbols/customSymbol.d.ts +0 -25
- package/dist/src/data/symbols/customSymbol.js +0 -45
- package/dist/src/data/symbols/customTextSymbol.d.ts +0 -35
- package/dist/src/data/symbols/customTextSymbol.js +0 -95
- package/dist/src/data/symbols/dartSymbol.d.ts +0 -36
- package/dist/src/data/symbols/dartSymbol.js +0 -96
- package/dist/src/data/symbols/directionLinkerSymbol.d.ts +0 -29
- package/dist/src/data/symbols/directionLinkerSymbol.js +0 -232
- package/dist/src/data/symbols/everyLetterSymbol.d.ts +0 -32
- package/dist/src/data/symbols/everyLetterSymbol.js +0 -119
- package/dist/src/data/symbols/focusSymbol.d.ts +0 -40
- package/dist/src/data/symbols/focusSymbol.js +0 -159
- package/dist/src/data/symbols/galaxySymbol.d.ts +0 -27
- package/dist/src/data/symbols/galaxySymbol.js +0 -61
- package/dist/src/data/symbols/hiddenSymbol.d.ts +0 -38
- package/dist/src/data/symbols/hiddenSymbol.js +0 -113
- package/dist/src/data/symbols/houseSymbol.d.ts +0 -33
- package/dist/src/data/symbols/houseSymbol.js +0 -104
- package/dist/src/data/symbols/index.d.ts +0 -3
- package/dist/src/data/symbols/index.js +0 -10
- package/dist/src/data/symbols/letterSymbol.d.ts +0 -32
- package/dist/src/data/symbols/letterSymbol.js +0 -118
- package/dist/src/data/symbols/lotusSymbol.d.ts +0 -30
- package/dist/src/data/symbols/lotusSymbol.js +0 -132
- package/dist/src/data/symbols/minesweeperSymbol.d.ts +0 -33
- package/dist/src/data/symbols/minesweeperSymbol.js +0 -106
- package/dist/src/data/symbols/myopiaSymbol.d.ts +0 -37
- package/dist/src/data/symbols/myopiaSymbol.js +0 -182
- package/dist/src/data/symbols/numberSymbol.d.ts +0 -19
- package/dist/src/data/symbols/numberSymbol.js +0 -32
- package/dist/src/data/symbols/symbol.d.ts +0 -29
- package/dist/src/data/symbols/symbol.js +0 -87
- package/dist/src/data/symbols/symbols.gen.d.ts +0 -15
- package/dist/src/data/symbols/symbols.gen.js +0 -19
- package/dist/src/data/symbols/unsupportedSymbol.d.ts +0 -23
- package/dist/src/data/symbols/unsupportedSymbol.js +0 -47
- package/dist/src/data/symbols/viewpointSymbol.d.ts +0 -32
- package/dist/src/data/symbols/viewpointSymbol.js +0 -95
- package/dist/src/data/tile.d.ts +0 -26
- package/dist/src/data/tile.js +0 -56
- package/dist/src/data/tileConnections.d.ts +0 -25
- package/dist/src/data/tileConnections.js +0 -74
- package/dist/src/data/validate.d.ts +0 -5
- package/dist/src/data/validate.js +0 -131
- package/dist/src/data/validateAsync.d.ts +0 -15
- package/dist/src/data/validateAsync.js +0 -71
- package/dist/src/data/validateAsyncWorker.d.ts +0 -1
- package/dist/src/data/validateAsyncWorker.js +0 -9
- package/dist/src/index.d.ts +0 -109
- package/dist/src/index.js +0 -112
- package/dist/src/polyfill/streamPolyfill.d.ts +0 -2
- package/dist/src/polyfill/streamPolyfill.js +0 -1
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import { ConfigType } from '../config.js';
|
|
2
|
-
import GridData from '../grid.js';
|
|
3
|
-
import { resize } from '../dataHelper.js';
|
|
4
|
-
import { Color, Instrument, MajorRule, State, } from '../primitives.js';
|
|
5
|
-
import CustomIconSymbol from '../symbols/customIconSymbol.js';
|
|
6
|
-
import { ControlLine, Row } from './musicControlLine.js';
|
|
7
|
-
import Rule from './rule.js';
|
|
8
|
-
const DEFAULT_SCALE = [
|
|
9
|
-
new Row('C5', Instrument.Piano, null),
|
|
10
|
-
new Row('B4', Instrument.Piano, null),
|
|
11
|
-
new Row('A4', Instrument.Piano, null),
|
|
12
|
-
new Row('G4', Instrument.Piano, null),
|
|
13
|
-
new Row('F4', Instrument.Piano, null),
|
|
14
|
-
new Row('E4', Instrument.Piano, null),
|
|
15
|
-
new Row('D4', Instrument.Piano, null),
|
|
16
|
-
new Row('C4', Instrument.Piano, null),
|
|
17
|
-
];
|
|
18
|
-
export default class MusicGridRule extends Rule {
|
|
19
|
-
controlLines;
|
|
20
|
-
track;
|
|
21
|
-
normalizeVelocity;
|
|
22
|
-
title = 'Music Grid';
|
|
23
|
-
get configExplanation() {
|
|
24
|
-
return 'Solve the grid by listening to the solution being played back.';
|
|
25
|
-
}
|
|
26
|
-
static EXAMPLE_GRID = Object.freeze(GridData.create(['.']).addSymbol(new CustomIconSymbol('', GridData.create([]), 0, 0, 'MdMusicNote')));
|
|
27
|
-
static CONFIGS = Object.freeze([
|
|
28
|
-
{
|
|
29
|
-
type: ConfigType.ControlLines,
|
|
30
|
-
default: [new ControlLine(0, 120, false, false, DEFAULT_SCALE)],
|
|
31
|
-
field: 'controlLines',
|
|
32
|
-
description: 'Control Lines',
|
|
33
|
-
configurable: false,
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
type: ConfigType.NullableGrid,
|
|
37
|
-
default: null,
|
|
38
|
-
nonNullDefault: GridData.create([
|
|
39
|
-
'wwwww',
|
|
40
|
-
'wwwww',
|
|
41
|
-
'wwwww',
|
|
42
|
-
'wwwww',
|
|
43
|
-
]).addRule(new MusicGridRule([new ControlLine(0, 120, false, false, DEFAULT_SCALE)], null)),
|
|
44
|
-
field: 'track',
|
|
45
|
-
description: 'Track',
|
|
46
|
-
explanation: 'If set, this grid will be played instead of the solution.',
|
|
47
|
-
configurable: true,
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
type: ConfigType.Boolean,
|
|
51
|
-
default: true,
|
|
52
|
-
field: 'normalizeVelocity',
|
|
53
|
-
description: 'Normalize Velocity',
|
|
54
|
-
explanation: 'Whether to adjust note velocities by their pitch such that every note has the same perceived loudness.',
|
|
55
|
-
configurable: true,
|
|
56
|
-
},
|
|
57
|
-
]);
|
|
58
|
-
static SEARCH_VARIANTS = [
|
|
59
|
-
new MusicGridRule([new ControlLine(0, 120, false, false, DEFAULT_SCALE)], null).searchVariant(),
|
|
60
|
-
];
|
|
61
|
-
/**
|
|
62
|
-
* **Music Grid: Listen to the solution**
|
|
63
|
-
* @param controlLines Denote changes in the playback settings. At least one control line at column 0 should be present to enable playback.
|
|
64
|
-
* @param track The grid to be played when "listen" is clicked. Set as null to play the solution.
|
|
65
|
-
* @param normalizeVelocity Whether to normalize the velocity of the notes by their pitch such that lower notes are played softer.
|
|
66
|
-
*/
|
|
67
|
-
constructor(controlLines, track, normalizeVelocity = true) {
|
|
68
|
-
super();
|
|
69
|
-
this.controlLines = controlLines;
|
|
70
|
-
this.track = track;
|
|
71
|
-
this.normalizeVelocity = normalizeVelocity;
|
|
72
|
-
this.controlLines = MusicGridRule.deduplicateControlLines(controlLines);
|
|
73
|
-
this.track = track;
|
|
74
|
-
this.normalizeVelocity = normalizeVelocity;
|
|
75
|
-
}
|
|
76
|
-
get id() {
|
|
77
|
-
return MajorRule.MusicGrid;
|
|
78
|
-
}
|
|
79
|
-
get explanation() {
|
|
80
|
-
return `*Music Grid:* Listen to the solution`;
|
|
81
|
-
}
|
|
82
|
-
get configs() {
|
|
83
|
-
return MusicGridRule.CONFIGS;
|
|
84
|
-
}
|
|
85
|
-
createExampleGrid() {
|
|
86
|
-
return MusicGridRule.EXAMPLE_GRID;
|
|
87
|
-
}
|
|
88
|
-
get searchVariants() {
|
|
89
|
-
return MusicGridRule.SEARCH_VARIANTS;
|
|
90
|
-
}
|
|
91
|
-
validateGrid(_grid) {
|
|
92
|
-
return { state: State.Incomplete };
|
|
93
|
-
}
|
|
94
|
-
onSetGrid(_oldGrid, newGrid, _solution) {
|
|
95
|
-
if (newGrid.getTileCount(true, undefined, Color.Gray) === 0)
|
|
96
|
-
return newGrid;
|
|
97
|
-
const tiles = newGrid.tiles.map(row => row.map(tile => tile.color === Color.Gray ? tile.withColor(Color.Light) : tile));
|
|
98
|
-
return newGrid.copyWith({ tiles });
|
|
99
|
-
}
|
|
100
|
-
onGridChange(newGrid) {
|
|
101
|
-
if (this.controlLines.length === 0)
|
|
102
|
-
return this;
|
|
103
|
-
if (newGrid.height === this.controlLines[0].rows.length &&
|
|
104
|
-
!this.controlLines.some(line => line.column >= newGrid.width))
|
|
105
|
-
return this;
|
|
106
|
-
const controlLines = this.controlLines
|
|
107
|
-
.filter(line => line.column < newGrid.width)
|
|
108
|
-
.map(line => line.withRows(resize(line.rows, newGrid.height, () => new Row(null, null, null))));
|
|
109
|
-
return this.copyWith({ controlLines });
|
|
110
|
-
}
|
|
111
|
-
onGridResize(_grid, mode, direction, index) {
|
|
112
|
-
if (mode === 'insert') {
|
|
113
|
-
if (direction === 'row') {
|
|
114
|
-
return this.copyWith({
|
|
115
|
-
controlLines: this.controlLines.map(line => {
|
|
116
|
-
const rows = line.rows.slice();
|
|
117
|
-
rows.splice(index, 0, new Row(null, null, null));
|
|
118
|
-
return line.withRows(rows);
|
|
119
|
-
}),
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
else if (direction === 'column') {
|
|
123
|
-
return this.copyWith({
|
|
124
|
-
controlLines: this.controlLines.map(line => line.column >= index ? line.withColumn(line.column + 1) : line),
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
else if (mode === 'remove') {
|
|
129
|
-
if (direction === 'row') {
|
|
130
|
-
return this.copyWith({
|
|
131
|
-
controlLines: this.controlLines.map(line => line.withRows(line.rows.filter((_, idx) => idx !== index))),
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
else if (direction === 'column') {
|
|
135
|
-
const lines = [];
|
|
136
|
-
for (const line of this.controlLines) {
|
|
137
|
-
if (line.column === index) {
|
|
138
|
-
const nextLine = this.controlLines.find(l => l.column === index + 1);
|
|
139
|
-
if (nextLine) {
|
|
140
|
-
lines.push(MusicGridRule.mergeControlLines(line, nextLine));
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
lines.push(line.withColumn(index));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
else if (line.column > index) {
|
|
147
|
-
lines.push(line.withColumn(line.column - 1));
|
|
148
|
-
}
|
|
149
|
-
else {
|
|
150
|
-
lines.push(line);
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
return this.copyWith({ controlLines: lines });
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
return this;
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Add or replace a control line.
|
|
160
|
-
* @param controlLine The control line to set.
|
|
161
|
-
* @returns A new rule with the control line set.
|
|
162
|
-
*/
|
|
163
|
-
setControlLine(controlLine) {
|
|
164
|
-
const controlLines = this.controlLines.filter(line => line.column !== controlLine.column);
|
|
165
|
-
return this.copyWith({
|
|
166
|
-
controlLines: [...controlLines, controlLine].sort((a, b) => a.column - b.column),
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
withTrack(track) {
|
|
170
|
-
return this.copyWith({ track });
|
|
171
|
-
}
|
|
172
|
-
copyWith({ controlLines, track, normalizeVelocity, }) {
|
|
173
|
-
return new MusicGridRule(controlLines ?? this.controlLines, track !== undefined ? track : this.track, normalizeVelocity ?? this.normalizeVelocity);
|
|
174
|
-
}
|
|
175
|
-
get validateWithSolution() {
|
|
176
|
-
return true;
|
|
177
|
-
}
|
|
178
|
-
get isSingleton() {
|
|
179
|
-
return true;
|
|
180
|
-
}
|
|
181
|
-
static mergeControlLines(...lines) {
|
|
182
|
-
const rows = Array.from({ length: Math.max(...lines.map(l => l.rows.length)) }, (_, idx) => {
|
|
183
|
-
const note = lines
|
|
184
|
-
.map(l => l.rows[idx]?.note)
|
|
185
|
-
.reduce((a, b) => b ?? a, null);
|
|
186
|
-
const instrument = lines
|
|
187
|
-
.map(l => l.rows[idx]?.instrument)
|
|
188
|
-
.reduce((a, b) => b ?? a, null);
|
|
189
|
-
const velocity = lines
|
|
190
|
-
.map(l => l.rows[idx]?.velocity)
|
|
191
|
-
.reduce((a, b) => b ?? a, null);
|
|
192
|
-
return new Row(note, instrument, velocity);
|
|
193
|
-
});
|
|
194
|
-
const bpm = lines.map(l => l.bpm).reduce((a, b) => b ?? a, null);
|
|
195
|
-
const pedal = lines.map(l => l.pedal).reduce((a, b) => b ?? a, null);
|
|
196
|
-
const checkpoint = lines.some(l => l.checkpoint);
|
|
197
|
-
return new ControlLine(lines[0].column, bpm, pedal, checkpoint, rows);
|
|
198
|
-
}
|
|
199
|
-
static deduplicateControlLines(lines) {
|
|
200
|
-
const columns = new Map();
|
|
201
|
-
for (const line of lines) {
|
|
202
|
-
if (!columns.has(line.column)) {
|
|
203
|
-
columns.set(line.column, [line]);
|
|
204
|
-
}
|
|
205
|
-
else {
|
|
206
|
-
columns.get(line.column).push(line);
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return Array.from(columns.values()).map(lines => lines.length > 1 ? MusicGridRule.mergeControlLines(...lines) : lines[0]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
export const instance = new MusicGridRule([new ControlLine(0, 120, false, false, DEFAULT_SCALE)], null);
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { AnyConfig } from '../config.js';
|
|
2
|
-
import { FinalValidationHandler } from '../events/onFinalValidation.js';
|
|
3
|
-
import { GridChangeHandler } from '../events/onGridChange.js';
|
|
4
|
-
import { GridResizeHandler } from '../events/onGridResize.js';
|
|
5
|
-
import GridData from '../grid.js';
|
|
6
|
-
import { GridState, RuleState } from '../primitives.js';
|
|
7
|
-
import Rule, { SearchVariant } from './rule.js';
|
|
8
|
-
export default class MysteryRule extends Rule implements FinalValidationHandler, GridChangeHandler, GridResizeHandler {
|
|
9
|
-
readonly solution: GridData;
|
|
10
|
-
readonly visible: boolean;
|
|
11
|
-
readonly title = "Alternate Solution";
|
|
12
|
-
get configExplanation(): string;
|
|
13
|
-
private static readonly EXAMPLE_GRID;
|
|
14
|
-
private static readonly CONFIGS;
|
|
15
|
-
private static readonly SEARCH_VARIANTS;
|
|
16
|
-
/**
|
|
17
|
-
* **Mystery: alternate solution**
|
|
18
|
-
*/
|
|
19
|
-
constructor(solution: GridData, visible: boolean);
|
|
20
|
-
get id(): string;
|
|
21
|
-
get explanation(): string;
|
|
22
|
-
get visibleWhenSolving(): boolean;
|
|
23
|
-
get configs(): readonly AnyConfig[] | null;
|
|
24
|
-
createExampleGrid(): GridData;
|
|
25
|
-
get searchVariants(): SearchVariant[];
|
|
26
|
-
validateGrid(grid: GridData): RuleState;
|
|
27
|
-
get necessaryForCompletion(): boolean;
|
|
28
|
-
onFinalValidation(grid: GridData, _solution: GridData | null, state: GridState): GridState;
|
|
29
|
-
onGridChange(newGrid: GridData): this;
|
|
30
|
-
onGridResize(_grid: GridData, mode: 'insert' | 'remove', direction: 'row' | 'column', index: number): this | null;
|
|
31
|
-
copyWith({ solution, visible, }: {
|
|
32
|
-
solution?: GridData;
|
|
33
|
-
visible?: boolean;
|
|
34
|
-
}): this;
|
|
35
|
-
withSolution(solution: GridData): this;
|
|
36
|
-
withVisible(visible: boolean): this;
|
|
37
|
-
static cleanSolution(solution: GridData, baseGrid?: GridData): GridData;
|
|
38
|
-
}
|
|
39
|
-
export declare const instance: MysteryRule;
|
|
@@ -1,146 +0,0 @@
|
|
|
1
|
-
import { ConfigType } from '../config.js';
|
|
2
|
-
import GridData from '../grid.js';
|
|
3
|
-
import { array } from '../dataHelper.js';
|
|
4
|
-
import { Color, State } from '../primitives.js';
|
|
5
|
-
import CustomTextSymbol from '../symbols/customTextSymbol.js';
|
|
6
|
-
import Rule from './rule.js';
|
|
7
|
-
export default class MysteryRule extends Rule {
|
|
8
|
-
solution;
|
|
9
|
-
visible;
|
|
10
|
-
title = 'Alternate Solution';
|
|
11
|
-
get configExplanation() {
|
|
12
|
-
return 'You may use multiple instances of this rule to provide multiple alternate solutions.';
|
|
13
|
-
}
|
|
14
|
-
static EXAMPLE_GRID = Object.freeze(GridData.create(['.']).addSymbol(new CustomTextSymbol('', GridData.create([]), 0, 0, '?')));
|
|
15
|
-
static CONFIGS = Object.freeze([
|
|
16
|
-
{
|
|
17
|
-
type: ConfigType.Tile,
|
|
18
|
-
default: MysteryRule.EXAMPLE_GRID,
|
|
19
|
-
resizable: false,
|
|
20
|
-
field: 'solution',
|
|
21
|
-
description: 'Solution',
|
|
22
|
-
explanation: 'The alternate solution to the puzzle. Does not need to satisfy puzzle rules / symbols.',
|
|
23
|
-
configurable: true,
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
type: ConfigType.Boolean,
|
|
27
|
-
default: true,
|
|
28
|
-
field: 'visible',
|
|
29
|
-
description: 'Visible',
|
|
30
|
-
explanation: 'Display this rule to the player.',
|
|
31
|
-
configurable: true,
|
|
32
|
-
},
|
|
33
|
-
]);
|
|
34
|
-
static SEARCH_VARIANTS = [
|
|
35
|
-
new MysteryRule(MysteryRule.EXAMPLE_GRID, true).searchVariant(),
|
|
36
|
-
];
|
|
37
|
-
/**
|
|
38
|
-
* **Mystery: alternate solution**
|
|
39
|
-
*/
|
|
40
|
-
constructor(solution, visible) {
|
|
41
|
-
super();
|
|
42
|
-
this.solution = solution;
|
|
43
|
-
this.visible = visible;
|
|
44
|
-
this.solution = MysteryRule.cleanSolution(solution);
|
|
45
|
-
this.visible = visible;
|
|
46
|
-
}
|
|
47
|
-
get id() {
|
|
48
|
-
return `mystery`;
|
|
49
|
-
}
|
|
50
|
-
get explanation() {
|
|
51
|
-
return `*Mystery:* Alternate solution`;
|
|
52
|
-
}
|
|
53
|
-
get visibleWhenSolving() {
|
|
54
|
-
return this.visible;
|
|
55
|
-
}
|
|
56
|
-
get configs() {
|
|
57
|
-
return MysteryRule.CONFIGS;
|
|
58
|
-
}
|
|
59
|
-
createExampleGrid() {
|
|
60
|
-
return MysteryRule.EXAMPLE_GRID;
|
|
61
|
-
}
|
|
62
|
-
get searchVariants() {
|
|
63
|
-
return MysteryRule.SEARCH_VARIANTS;
|
|
64
|
-
}
|
|
65
|
-
validateGrid(grid) {
|
|
66
|
-
if (grid.colorEquals(this.solution))
|
|
67
|
-
return { state: State.Satisfied };
|
|
68
|
-
return { state: State.Incomplete };
|
|
69
|
-
}
|
|
70
|
-
get necessaryForCompletion() {
|
|
71
|
-
return false;
|
|
72
|
-
}
|
|
73
|
-
onFinalValidation(grid, _solution, state) {
|
|
74
|
-
if (State.isSatisfied(state.final))
|
|
75
|
-
return state;
|
|
76
|
-
if (grid.colorEquals(this.solution))
|
|
77
|
-
return {
|
|
78
|
-
final: State.Satisfied,
|
|
79
|
-
symbols: state.symbols,
|
|
80
|
-
rules: state.rules,
|
|
81
|
-
};
|
|
82
|
-
return state;
|
|
83
|
-
}
|
|
84
|
-
onGridChange(newGrid) {
|
|
85
|
-
if (newGrid.width === this.solution.width &&
|
|
86
|
-
newGrid.height === this.solution.height) {
|
|
87
|
-
if (!newGrid.tiles.some((row, y) => row.some((tile, x) => {
|
|
88
|
-
const solutionTile = this.solution.getTile(x, y);
|
|
89
|
-
if (solutionTile.exists !== tile.exists)
|
|
90
|
-
return true;
|
|
91
|
-
if (solutionTile.fixed !== tile.fixed)
|
|
92
|
-
return true;
|
|
93
|
-
if (solutionTile.exists &&
|
|
94
|
-
solutionTile.fixed &&
|
|
95
|
-
solutionTile.color !== tile.color)
|
|
96
|
-
return true;
|
|
97
|
-
return false;
|
|
98
|
-
})))
|
|
99
|
-
return this;
|
|
100
|
-
}
|
|
101
|
-
return this.withSolution(MysteryRule.cleanSolution(this.solution, newGrid));
|
|
102
|
-
}
|
|
103
|
-
onGridResize(_grid, mode, direction, index) {
|
|
104
|
-
if (mode === 'insert') {
|
|
105
|
-
if (direction === 'row') {
|
|
106
|
-
return this.withSolution(this.solution.insertRow(index));
|
|
107
|
-
}
|
|
108
|
-
else if (direction === 'column') {
|
|
109
|
-
return this.withSolution(this.solution.insertColumn(index));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
else if (mode === 'remove') {
|
|
113
|
-
if (direction === 'row') {
|
|
114
|
-
return this.withSolution(this.solution.removeRow(index));
|
|
115
|
-
}
|
|
116
|
-
else if (direction === 'column') {
|
|
117
|
-
return this.withSolution(this.solution.removeColumn(index));
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return this;
|
|
121
|
-
}
|
|
122
|
-
copyWith({ solution, visible, }) {
|
|
123
|
-
return new MysteryRule(solution ?? this.solution, visible ?? this.visible);
|
|
124
|
-
}
|
|
125
|
-
withSolution(solution) {
|
|
126
|
-
return this.copyWith({ solution });
|
|
127
|
-
}
|
|
128
|
-
withVisible(visible) {
|
|
129
|
-
return this.copyWith({ visible });
|
|
130
|
-
}
|
|
131
|
-
static cleanSolution(solution, baseGrid) {
|
|
132
|
-
const tiles = baseGrid
|
|
133
|
-
? array(baseGrid.width, baseGrid.height, (x, y) => {
|
|
134
|
-
const tile = baseGrid.getTile(x, y);
|
|
135
|
-
if (!tile.exists || tile.fixed)
|
|
136
|
-
return tile;
|
|
137
|
-
const solutionTile = solution.getTile(x, y);
|
|
138
|
-
if (!solutionTile.exists || solutionTile.color === Color.Gray)
|
|
139
|
-
return tile;
|
|
140
|
-
return tile.withColor(solutionTile.color);
|
|
141
|
-
})
|
|
142
|
-
: solution.tiles;
|
|
143
|
-
return GridData.create(baseGrid?.width ?? solution.width, baseGrid?.height ?? solution.height, tiles);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
export const instance = new MysteryRule(GridData.create([]), true);
|
|
@@ -1,29 +0,0 @@
|
|
|
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 NoLoopsRule extends Rule {
|
|
6
|
-
readonly color: Color;
|
|
7
|
-
readonly title = "No Loops";
|
|
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
|
-
* **No loops in <color> cells**
|
|
14
|
-
*
|
|
15
|
-
* @param color - The color of the cells to check.
|
|
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: NoLoopsRule;
|
|
@@ -1,218 +0,0 @@
|
|
|
1
|
-
import { ConfigType } from '../config.js';
|
|
2
|
-
import GridData, { NEIGHBOR_OFFSETS, NEIGHBOR_OFFSETS_8 } from '../grid.js';
|
|
3
|
-
import { array } from '../dataHelper.js';
|
|
4
|
-
import { Color, State } from '../primitives.js';
|
|
5
|
-
import Rule from './rule.js';
|
|
6
|
-
import CustomIconSymbol from '../symbols/customIconSymbol.js';
|
|
7
|
-
export default class NoLoopsRule extends Rule {
|
|
8
|
-
color;
|
|
9
|
-
title = 'No Loops';
|
|
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(['bwwwb', 'bwbww', 'wwwwb', 'wbbww']).withSymbols([
|
|
21
|
-
new CustomIconSymbol('', GridData.create([]), 1, 0, 'MdClear'),
|
|
22
|
-
new CustomIconSymbol('', GridData.create([]), 2, 0, 'MdClear'),
|
|
23
|
-
new CustomIconSymbol('', GridData.create([]), 3, 0, 'MdClear'),
|
|
24
|
-
new CustomIconSymbol('', GridData.create([]), 3, 1, 'MdClear'),
|
|
25
|
-
new CustomIconSymbol('', GridData.create([]), 3, 2, 'MdClear'),
|
|
26
|
-
new CustomIconSymbol('', GridData.create([]), 2, 2, 'MdClear'),
|
|
27
|
-
new CustomIconSymbol('', GridData.create([]), 1, 2, 'MdClear'),
|
|
28
|
-
new CustomIconSymbol('', GridData.create([]), 1, 1, 'MdClear'),
|
|
29
|
-
]));
|
|
30
|
-
static EXAMPLE_GRID_DARK = Object.freeze(NoLoopsRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))));
|
|
31
|
-
static SEARCH_VARIANTS = [
|
|
32
|
-
new NoLoopsRule(Color.Light).searchVariant(),
|
|
33
|
-
new NoLoopsRule(Color.Dark).searchVariant(),
|
|
34
|
-
];
|
|
35
|
-
/**
|
|
36
|
-
* **No loops in <color> cells**
|
|
37
|
-
*
|
|
38
|
-
* @param color - The color of the cells to check.
|
|
39
|
-
*/
|
|
40
|
-
constructor(color) {
|
|
41
|
-
super();
|
|
42
|
-
this.color = color;
|
|
43
|
-
this.color = color;
|
|
44
|
-
}
|
|
45
|
-
get id() {
|
|
46
|
-
return `no_loops`;
|
|
47
|
-
}
|
|
48
|
-
get explanation() {
|
|
49
|
-
return `*No loops* in ${this.color} cells`;
|
|
50
|
-
}
|
|
51
|
-
get configs() {
|
|
52
|
-
return NoLoopsRule.CONFIGS;
|
|
53
|
-
}
|
|
54
|
-
createExampleGrid() {
|
|
55
|
-
return this.color === Color.Light
|
|
56
|
-
? NoLoopsRule.EXAMPLE_GRID_LIGHT
|
|
57
|
-
: NoLoopsRule.EXAMPLE_GRID_DARK;
|
|
58
|
-
}
|
|
59
|
-
get searchVariants() {
|
|
60
|
-
return NoLoopsRule.SEARCH_VARIANTS;
|
|
61
|
-
}
|
|
62
|
-
validateGrid(grid) {
|
|
63
|
-
// wrap-around grids require special consideration because the "islands" assumption of the
|
|
64
|
-
// algorithm below does not hold
|
|
65
|
-
if (grid.wrapAround.value) {
|
|
66
|
-
const visited = array(grid.width, grid.height, (i, j) => {
|
|
67
|
-
const tile = grid.getTile(i, j);
|
|
68
|
-
return (!tile.exists ||
|
|
69
|
-
(tile.color !== this.color && tile.color !== Color.Gray));
|
|
70
|
-
});
|
|
71
|
-
while (true) {
|
|
72
|
-
const seed = grid.find((tile, x, y) => !visited[y][x] && tile.color === this.color);
|
|
73
|
-
if (!seed)
|
|
74
|
-
break;
|
|
75
|
-
let invalid = false;
|
|
76
|
-
const positions = [];
|
|
77
|
-
const stack = [[seed, null]];
|
|
78
|
-
while (stack.length > 0) {
|
|
79
|
-
const [{ x, y }, from] = stack.pop();
|
|
80
|
-
const { x: arrX, y: arrY } = grid.toArrayCoordinates(x, y);
|
|
81
|
-
positions.push({ x: arrX, y: arrY });
|
|
82
|
-
if (visited[arrY][arrX]) {
|
|
83
|
-
invalid = true;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
visited[arrY][arrX] = true;
|
|
87
|
-
for (const offset of NEIGHBOR_OFFSETS) {
|
|
88
|
-
if (-offset.x === from?.x && -offset.y === from?.y)
|
|
89
|
-
continue;
|
|
90
|
-
const next = { x: x + offset.x, y: y + offset.y };
|
|
91
|
-
if (grid.isPositionValid(next.x, next.y)) {
|
|
92
|
-
const nextTile = grid.getTile(next.x, next.y);
|
|
93
|
-
if (nextTile.exists && nextTile.color === this.color)
|
|
94
|
-
stack.push([next, offset]);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (invalid) {
|
|
99
|
-
return {
|
|
100
|
-
state: State.Error,
|
|
101
|
-
positions,
|
|
102
|
-
};
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return {
|
|
106
|
-
state: visited.some(row => row.some(v => !v))
|
|
107
|
-
? State.Incomplete
|
|
108
|
-
: State.Satisfied,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
// special handling for 2x2 loops
|
|
112
|
-
for (let y = 0; y < grid.height; y++) {
|
|
113
|
-
for (let x = 0; x < grid.width; x++) {
|
|
114
|
-
const tlTile = grid.getTile(x, y);
|
|
115
|
-
const trTile = grid.getTile(x + 1, y);
|
|
116
|
-
const blTile = grid.getTile(x, y + 1);
|
|
117
|
-
const brTile = grid.getTile(x + 1, y + 1);
|
|
118
|
-
if (tlTile.exists &&
|
|
119
|
-
tlTile.color === this.color &&
|
|
120
|
-
trTile.exists &&
|
|
121
|
-
trTile.color === this.color &&
|
|
122
|
-
blTile.exists &&
|
|
123
|
-
blTile.color === this.color &&
|
|
124
|
-
brTile.exists &&
|
|
125
|
-
brTile.color === this.color) {
|
|
126
|
-
const positions = [
|
|
127
|
-
grid.toArrayCoordinates(x, y),
|
|
128
|
-
grid.toArrayCoordinates(x + 1, y),
|
|
129
|
-
grid.toArrayCoordinates(x, y + 1),
|
|
130
|
-
grid.toArrayCoordinates(x + 1, y + 1),
|
|
131
|
-
];
|
|
132
|
-
return {
|
|
133
|
-
state: State.Error,
|
|
134
|
-
positions,
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
// general case for non-wrap-around grids: a loop must form an elcosed island that does not touch the grid edge
|
|
140
|
-
const visited = array(grid.width, grid.height, (i, j) => {
|
|
141
|
-
const tile = grid.getTile(i, j);
|
|
142
|
-
return tile.exists && tile.color === this.color;
|
|
143
|
-
});
|
|
144
|
-
const shape = array(grid.width, grid.height, () => false);
|
|
145
|
-
let complete = true;
|
|
146
|
-
while (true) {
|
|
147
|
-
const seed = grid.find((tile, x, y) => !visited[y][x] && (!tile.exists || tile.color !== this.color));
|
|
148
|
-
shape.forEach(row => row.fill(false));
|
|
149
|
-
if (!seed)
|
|
150
|
-
break;
|
|
151
|
-
let isIsland = true;
|
|
152
|
-
const stack = [seed];
|
|
153
|
-
while (stack.length > 0) {
|
|
154
|
-
const { x, y } = stack.pop();
|
|
155
|
-
const { x: arrX, y: arrY } = grid.toArrayCoordinates(x, y);
|
|
156
|
-
const tile = grid.getTile(x, y);
|
|
157
|
-
if (tile.exists && tile.color === Color.Gray) {
|
|
158
|
-
complete = false;
|
|
159
|
-
}
|
|
160
|
-
if (visited[arrY][arrX]) {
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
visited[arrY][arrX] = true;
|
|
164
|
-
for (const offset of NEIGHBOR_OFFSETS_8) {
|
|
165
|
-
const next = { x: x + offset.x, y: y + offset.y };
|
|
166
|
-
const arrPos = grid.toArrayCoordinates(next.x, next.y);
|
|
167
|
-
if (grid.isPositionValid(next.x, next.y)) {
|
|
168
|
-
const nextTile = grid.getTile(next.x, next.y);
|
|
169
|
-
shape[arrPos.y][arrPos.x] = true;
|
|
170
|
-
if (!nextTile.exists || nextTile.color !== this.color) {
|
|
171
|
-
stack.push(arrPos);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
isIsland = false;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
if (isIsland) {
|
|
180
|
-
const loopPositions = [];
|
|
181
|
-
for (let y = 0; y < grid.height; y++) {
|
|
182
|
-
for (let x = 0; x < grid.width; x++) {
|
|
183
|
-
if (shape[y][x]) {
|
|
184
|
-
if (x > 0 &&
|
|
185
|
-
y > 0 &&
|
|
186
|
-
x < grid.width - 1 &&
|
|
187
|
-
y < grid.height - 1 &&
|
|
188
|
-
shape[y][x - 1] &&
|
|
189
|
-
shape[y - 1][x] &&
|
|
190
|
-
shape[y][x + 1] &&
|
|
191
|
-
shape[y + 1][x] &&
|
|
192
|
-
shape[y - 1][x - 1] &&
|
|
193
|
-
shape[y - 1][x + 1] &&
|
|
194
|
-
shape[y + 1][x - 1] &&
|
|
195
|
-
shape[y + 1][x + 1])
|
|
196
|
-
continue;
|
|
197
|
-
loopPositions.push({ x, y });
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
return {
|
|
202
|
-
state: State.Error,
|
|
203
|
-
positions: loopPositions,
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
return {
|
|
208
|
-
state: complete ? State.Satisfied : State.Incomplete,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
copyWith({ color }) {
|
|
212
|
-
return new NoLoopsRule(color ?? this.color);
|
|
213
|
-
}
|
|
214
|
-
withColor(color) {
|
|
215
|
-
return this.copyWith({ color });
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
export const instance = new NoLoopsRule(Color.Dark);
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { AnyConfig } from '../config.js';
|
|
2
|
-
import { SymbolValidationHandler } from '../events/onSymbolValidation.js';
|
|
3
|
-
import GridData from '../grid.js';
|
|
4
|
-
import { RuleState, State } from '../primitives.js';
|
|
5
|
-
import Symbol from '../symbols/symbol.js';
|
|
6
|
-
import Rule, { SearchVariant } from './rule.js';
|
|
7
|
-
export default class OffByXRule extends Rule implements SymbolValidationHandler {
|
|
8
|
-
readonly number: number;
|
|
9
|
-
readonly title = "Off By X";
|
|
10
|
-
private static readonly CONFIGS;
|
|
11
|
-
private static readonly EXAMPLE_GRID;
|
|
12
|
-
private static readonly SEARCH_VARIANTS;
|
|
13
|
-
/**
|
|
14
|
-
* **All numbers are off by <number>**
|
|
15
|
-
*
|
|
16
|
-
* @param number - The number that all cells are off by.
|
|
17
|
-
*/
|
|
18
|
-
constructor(number: number);
|
|
19
|
-
get id(): string;
|
|
20
|
-
get explanation(): string;
|
|
21
|
-
get configs(): readonly AnyConfig[] | null;
|
|
22
|
-
createExampleGrid(): GridData;
|
|
23
|
-
get searchVariants(): SearchVariant[];
|
|
24
|
-
validateGrid(grid: GridData): RuleState;
|
|
25
|
-
onSymbolValidation(grid: GridData, symbol: Symbol, _validator: (grid: GridData) => State): State | undefined;
|
|
26
|
-
get isSingleton(): boolean;
|
|
27
|
-
copyWith({ number }: {
|
|
28
|
-
number?: number;
|
|
29
|
-
}): this;
|
|
30
|
-
withNumber(number: number): this;
|
|
31
|
-
}
|
|
32
|
-
export declare const instance: OffByXRule;
|