@logic-pad/core 0.4.2 → 0.4.5

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.
@@ -111,6 +111,7 @@ declare global {
111
111
  export declare enum Mode {
112
112
  Create = 'create',
113
113
  Solve = 'solve',
114
+ Perfection = 'perfection',
114
115
  }
115
116
  export declare class GridZones {
116
117
  readonly edges: readonly Edge[];
@@ -320,6 +321,10 @@ declare global {
320
321
  get validateWithSolution(): boolean;
321
322
  get necessaryForCompletion(): boolean;
322
323
  get visibleWhenSolving(): boolean;
324
+ /**
325
+ * Return a variant of this instruction that is suitable for the given mode.
326
+ */
327
+ abstract modeVariant(mode: Mode): Instruction | null;
323
328
  /**
324
329
  * Check if this instruction is equal to another instruction by comparing their IDs and configs.
325
330
  *
@@ -336,6 +341,7 @@ declare global {
336
341
  abstract validateGrid(grid: GridData): RuleState;
337
342
  abstract get searchVariants(): SearchVariant[];
338
343
  searchVariant(): SearchVariant;
344
+ modeVariant(_mode: Mode): Rule | null;
339
345
  /**
340
346
  * Whether only one instance of this rule is allowed in a grid.
341
347
  */
@@ -362,7 +368,8 @@ declare global {
362
368
  readonly x: number;
363
369
  readonly y: number;
364
370
  constructor(x: number, y: number);
365
- abstract validateSymbol(grid: GridData): State;
371
+ abstract validateSymbol(grid: GridData, solution: GridData | null): State;
372
+ modeVariant(_mode: Mode): Symbol$1 | null;
366
373
  onGridResize(
367
374
  _grid: GridData,
368
375
  mode: 'insert' | 'remove',
@@ -426,6 +433,11 @@ declare global {
426
433
  export declare function handlesSetGrid<T extends Instruction>(
427
434
  val: T
428
435
  ): val is T & SetGridHandler;
436
+ export declare function invokeSetGrid(
437
+ oldGrid: GridData,
438
+ newGrid: GridData,
439
+ solution: GridData | null
440
+ ): GridData;
429
441
  export declare class Row extends Configurable {
430
442
  /**
431
443
  * The note to play at this row, or null to keep the current note from the previous control line.
@@ -509,6 +521,7 @@ declare global {
509
521
  {
510
522
  readonly controlLines: readonly ControlLine[];
511
523
  readonly track: GridData | null;
524
+ readonly normalizeVelocity: boolean;
512
525
  private static readonly EXAMPLE_GRID;
513
526
  private static readonly CONFIGS;
514
527
  private static readonly SEARCH_VARIANTS;
@@ -516,8 +529,13 @@ declare global {
516
529
  * **Music Grid: Listen to the solution**
517
530
  * @param controlLines Denote changes in the playback settings. At least one control line at column 0 should be present to enable playback.
518
531
  * @param track The grid to be played when "listen" is clicked. Set as null to play the solution.
532
+ * @param normalizeVelocity Whether to normalize the velocity of the notes by their pitch such that lower notes are played softer.
519
533
  */
520
- constructor(controlLines: readonly ControlLine[], track: GridData | null);
534
+ constructor(
535
+ controlLines: readonly ControlLine[],
536
+ track: GridData | null,
537
+ normalizeVelocity?: boolean
538
+ );
521
539
  get id(): string;
522
540
  get explanation(): string;
523
541
  get configs(): readonly AnyConfig[] | null;
@@ -546,9 +564,11 @@ declare global {
546
564
  copyWith({
547
565
  controlLines,
548
566
  track,
567
+ normalizeVelocity,
549
568
  }: {
550
569
  controlLines?: readonly ControlLine[];
551
570
  track?: GridData | null;
571
+ normalizeVelocity?: boolean;
552
572
  }): this;
553
573
  get validateWithSolution(): boolean;
554
574
  get isSingleton(): boolean;
@@ -980,14 +1000,6 @@ declare global {
980
1000
  * @returns True if the grids are equal in size and tile colors, false otherwise.
981
1001
  */
982
1002
  colorEquals(grid: GridData): boolean;
983
- /**
984
- * Check if this grid conforms to the given solution, or an incomplete version of the solution.
985
- * Symbols and rules are not validated.
986
- *
987
- * @param solution The solution to compare with.
988
- * @returns True if the grid conforms to the solution, false otherwise.
989
- */
990
- solutionMatches(solution: GridData): boolean;
991
1003
  /**
992
1004
  * Check if this grid is equal to another grid in terms of size, tile colors, connections, symbols, and rules.
993
1005
  *
@@ -1190,12 +1202,14 @@ declare global {
1190
1202
  * Controls whether a symbol should be visible in the grid.
1191
1203
  *
1192
1204
  * @param grid The grid that is being displayed.
1205
+ * @param solution The solution grid, if it is available.
1193
1206
  * @param symbol The symbol that is being displayed.
1194
1207
  * @param editing Whether the grid is being edited.
1195
1208
  * @returns True if the symbol should be displayed, false otherwise. The symbol will not be displayed if any handler returns false.
1196
1209
  */
1197
1210
  onSymbolDisplay(
1198
1211
  grid: GridData,
1212
+ solution: GridData | null,
1199
1213
  symbol: Symbol$1,
1200
1214
  editing: boolean
1201
1215
  ): boolean;
@@ -1368,7 +1382,7 @@ declare global {
1368
1382
  private static readonly EXAMPLE_GRID_GRAY;
1369
1383
  private static readonly SEARCH_VARIANTS;
1370
1384
  /**
1371
- * **Every zone has the same number of &lt;color&gt; cells.**
1385
+ * **Zones of the same size have the same number of &lt;color&gt; cells.**
1372
1386
  *
1373
1387
  * @param color - The color of the cells to count.
1374
1388
  */
@@ -1483,6 +1497,7 @@ declare global {
1483
1497
  validateGrid(_grid: GridData): RuleState;
1484
1498
  get necessaryForCompletion(): boolean;
1485
1499
  get isSingleton(): boolean;
1500
+ modeVariant(mode: Mode): Rule | null;
1486
1501
  copyWith({
1487
1502
  count,
1488
1503
  regenInterval,
@@ -1573,12 +1588,15 @@ declare global {
1573
1588
  extends Rule
1574
1589
  implements SetGridHandler, FinalValidationHandler
1575
1590
  {
1591
+ readonly editor: boolean;
1576
1592
  private static readonly EXAMPLE_GRID;
1577
1593
  private static readonly SEARCH_VARIANTS;
1578
1594
  /**
1579
1595
  * **Quest for Perfection: cell colors are final**
1596
+ *
1597
+ * @param editor - whether to enable editor mode. This field is automatically set by the editor.
1580
1598
  */
1581
- constructor();
1599
+ constructor(editor?: boolean);
1582
1600
  get id(): string;
1583
1601
  get explanation(): string;
1584
1602
  get configs(): readonly AnyConfig[] | null;
@@ -1586,6 +1604,7 @@ declare global {
1586
1604
  get searchVariants(): SearchVariant[];
1587
1605
  get necessaryForCompletion(): boolean;
1588
1606
  get isSingleton(): boolean;
1607
+ modeVariant(mode: Mode): Rule | null;
1589
1608
  validateGrid(grid: GridData): RuleState;
1590
1609
  /**
1591
1610
  * If the grid passes validation but is different from the solution, indicate the error in the final state.
@@ -1596,6 +1615,8 @@ declare global {
1596
1615
  state: GridState
1597
1616
  ): GridState;
1598
1617
  private fixTiles;
1618
+ private isValid;
1619
+ private findSingleError;
1599
1620
  /**
1600
1621
  * Force all tiles to be fixed.
1601
1622
  *
@@ -1606,7 +1627,7 @@ declare global {
1606
1627
  newGrid: GridData,
1607
1628
  solution: GridData | null
1608
1629
  ): GridData;
1609
- copyWith(_: object): this;
1630
+ copyWith({ editor }: { editor?: boolean }): this;
1610
1631
  }
1611
1632
  export declare class RegionAreaRule extends Rule {
1612
1633
  readonly color: Color;
@@ -6017,7 +6038,6 @@ declare global {
6017
6038
  {
6018
6039
  readonly x: number;
6019
6040
  readonly y: number;
6020
- readonly color: Color;
6021
6041
  readonly revealLocation: boolean;
6022
6042
  private static readonly CONFIGS;
6023
6043
  private static readonly EXAMPLE_GRID;
@@ -6026,10 +6046,9 @@ declare global {
6026
6046
  *
6027
6047
  * @param x - The x-coordinate of the symbol.
6028
6048
  * @param y - The y-coordinate of the symbol.
6029
- * @param color - The target color of the cell.
6030
6049
  * @param revealLocation - Whether to reveal the location of the symbol.
6031
6050
  */
6032
- constructor(x: number, y: number, color: Color, revealLocation?: boolean);
6051
+ constructor(x: number, y: number, revealLocation?: boolean);
6033
6052
  get id(): string;
6034
6053
  get explanation(): string;
6035
6054
  get configs(): readonly AnyConfig[] | null;
@@ -6037,24 +6056,22 @@ declare global {
6037
6056
  get necessaryForCompletion(): boolean;
6038
6057
  get visibleWhenSolving(): boolean;
6039
6058
  get sortOrder(): number;
6040
- validateSymbol(grid: GridData): State;
6059
+ validateSymbol(grid: GridData, solution: GridData | null): State;
6041
6060
  onSymbolDisplay(
6042
6061
  grid: GridData,
6062
+ solution: GridData | null,
6043
6063
  symbol: Symbol$1,
6044
6064
  editing: boolean
6045
6065
  ): boolean;
6046
6066
  copyWith({
6047
6067
  x,
6048
6068
  y,
6049
- color,
6050
6069
  revealLocation,
6051
6070
  }: {
6052
6071
  x?: number;
6053
6072
  y?: number;
6054
- color?: Color;
6055
6073
  revealLocation?: boolean;
6056
6074
  }): this;
6057
- withColor(color: Color): this;
6058
6075
  withRevealLocation(revealLocation: boolean): this;
6059
6076
  }
6060
6077
  export declare const allSymbols: Map<string, Symbol$1>;
@@ -4,3 +4,4 @@ export interface SetGridHandler {
4
4
  onSetGrid(oldGrid: GridData, newGrid: GridData, solution: GridData | null): GridData;
5
5
  }
6
6
  export declare function handlesSetGrid<T extends Instruction>(val: T): val is T & SetGridHandler;
7
+ export declare function invokeSetGrid(oldGrid: GridData, newGrid: GridData, solution: GridData | null): GridData;
@@ -2,3 +2,18 @@ import { isEventHandler } from './eventHelper.js';
2
2
  export function handlesSetGrid(val) {
3
3
  return isEventHandler(val, 'onSetGrid');
4
4
  }
5
+ export function invokeSetGrid(oldGrid, newGrid, solution) {
6
+ newGrid.symbols.forEach(list => {
7
+ list.forEach(symbol => {
8
+ if (handlesSetGrid(symbol)) {
9
+ newGrid = symbol.onSetGrid(oldGrid, newGrid, solution);
10
+ }
11
+ });
12
+ });
13
+ newGrid.rules.forEach(rule => {
14
+ if (handlesSetGrid(rule)) {
15
+ newGrid = rule.onSetGrid(oldGrid, newGrid, solution);
16
+ }
17
+ });
18
+ return newGrid;
19
+ }
@@ -6,10 +6,11 @@ export interface SymbolDisplayHandler {
6
6
  * Controls whether a symbol should be visible in the grid.
7
7
  *
8
8
  * @param grid The grid that is being displayed.
9
+ * @param solution The solution grid, if it is available.
9
10
  * @param symbol The symbol that is being displayed.
10
11
  * @param editing Whether the grid is being edited.
11
12
  * @returns True if the symbol should be displayed, false otherwise. The symbol will not be displayed if any handler returns false.
12
13
  */
13
- onSymbolDisplay(grid: GridData, symbol: Symbol, editing: boolean): boolean;
14
+ onSymbolDisplay(grid: GridData, solution: GridData | null, symbol: Symbol, editing: boolean): boolean;
14
15
  }
15
16
  export declare function handlesSymbolDisplay<T extends Instruction>(val: T): val is T & SymbolDisplayHandler;
@@ -332,14 +332,6 @@ export default class GridData {
332
332
  * @returns True if the grids are equal in size and tile colors, false otherwise.
333
333
  */
334
334
  colorEquals(grid: GridData): boolean;
335
- /**
336
- * Check if this grid conforms to the given solution, or an incomplete version of the solution.
337
- * Symbols and rules are not validated.
338
- *
339
- * @param solution The solution to compare with.
340
- * @returns True if the grid conforms to the solution, false otherwise.
341
- */
342
- solutionMatches(solution: GridData): boolean;
343
335
  /**
344
336
  * Check if this grid is equal to another grid in terms of size, tile colors, connections, symbols, and rules.
345
337
  *
package/dist/data/grid.js CHANGED
@@ -855,29 +855,6 @@ export default class GridData {
855
855
  this.tiles.every((row, y) => row.every((tile, x) => (!tile.exists && !grid.getTile(x, y).exists) ||
856
856
  tile.color === grid.getTile(x, y).color)));
857
857
  }
858
- /**
859
- * Check if this grid conforms to the given solution, or an incomplete version of the solution.
860
- * Symbols and rules are not validated.
861
- *
862
- * @param solution The solution to compare with.
863
- * @returns True if the grid conforms to the solution, false otherwise.
864
- */
865
- solutionMatches(solution) {
866
- if (this.width !== solution.width)
867
- return false;
868
- if (this.height !== solution.height)
869
- return false;
870
- return this.tiles.every((row, y) => row.every((tile, x) => {
871
- const solutionTile = solution.getTile(x, y);
872
- if (!solutionTile.exists)
873
- return true;
874
- if (tile.color === Color.Gray)
875
- return true;
876
- if (solutionTile.color !== tile.color)
877
- return false;
878
- return true;
879
- }));
880
- }
881
858
  /**
882
859
  * Check if this grid is equal to another grid in terms of size, tile colors, connections, symbols, and rules.
883
860
  *
@@ -1,5 +1,6 @@
1
1
  import Configurable from './configurable.js';
2
2
  import GridData from './grid.js';
3
+ import { Mode } from './primitives.js';
3
4
  export default abstract class Instruction extends Configurable {
4
5
  abstract get id(): string;
5
6
  abstract get explanation(): string;
@@ -10,6 +11,10 @@ export default abstract class Instruction extends Configurable {
10
11
  get validateWithSolution(): boolean;
11
12
  get necessaryForCompletion(): boolean;
12
13
  get visibleWhenSolving(): boolean;
14
+ /**
15
+ * Return a variant of this instruction that is suitable for the given mode.
16
+ */
17
+ abstract modeVariant(mode: Mode): Instruction | null;
13
18
  /**
14
19
  * Check if this instruction is equal to another instruction by comparing their IDs and configs.
15
20
  *
@@ -89,5 +89,6 @@ export declare function orientationToggle(...orientations: readonly Orientation[
89
89
  };
90
90
  export declare enum Mode {
91
91
  Create = "create",
92
- Solve = "solve"
92
+ Solve = "solve",
93
+ Perfection = "perfection"
93
94
  }
@@ -96,4 +96,5 @@ export var Mode;
96
96
  (function (Mode) {
97
97
  Mode["Create"] = "create";
98
98
  Mode["Solve"] = "solve";
99
+ Mode["Perfection"] = "perfection";
99
100
  })(Mode || (Mode = {}));
@@ -10,7 +10,7 @@ export default class CellCountPerZoneRule extends Rule {
10
10
  private static readonly EXAMPLE_GRID_GRAY;
11
11
  private static readonly SEARCH_VARIANTS;
12
12
  /**
13
- * **Every zone has the same number of &lt;color&gt; cells.**
13
+ * **Zones of the same size have the same number of &lt;color&gt; cells.**
14
14
  *
15
15
  * @param color - The color of the cells to count.
16
16
  */
@@ -6,7 +6,7 @@ import { Color, State } from '../primitives.js';
6
6
  import Rule from './rule.js';
7
7
  class CellCountPerZoneRule extends Rule {
8
8
  /**
9
- * **Every zone has the same number of &lt;color&gt; cells.**
9
+ * **Zones of the same size have the same number of &lt;color&gt; cells.**
10
10
  *
11
11
  * @param color - The color of the cells to count.
12
12
  */
@@ -24,7 +24,7 @@ class CellCountPerZoneRule extends Rule {
24
24
  return `zone_cell_count`;
25
25
  }
26
26
  get explanation() {
27
- return `Every zone has the same number of ${this.color} cells`;
27
+ return `Zones of the same size have the same number of ${this.color} cells`;
28
28
  }
29
29
  get configs() {
30
30
  return CellCountPerZoneRule.CONFIGS;
@@ -91,6 +91,7 @@ class CellCountPerZoneRule extends Rule {
91
91
  }
92
92
  else {
93
93
  const errorZone = zones.find(z => zones.some(zz => zz !== z &&
94
+ zz.positions.length === z.positions.length &&
94
95
  (zz.completed > z.completed + z.possible ||
95
96
  zz.completed + zz.possible < z.completed)));
96
97
  if (errorZone) {
@@ -137,6 +138,10 @@ Object.defineProperty(CellCountPerZoneRule, "EXAMPLE_GRID_LIGHT", {
137
138
  { x1: 2, y1: 1, x2: 2, y2: 2 },
138
139
  { x1: 3, y1: 1, x2: 3, y2: 2 },
139
140
  { x1: 4, y1: 1, x2: 4, y2: 2 },
141
+ { x1: 1, y1: 0, x2: 2, y2: 0 },
142
+ { x1: 1, y1: 1, x2: 2, y2: 1 },
143
+ { x1: 2, y1: 2, x2: 3, y2: 2 },
144
+ { x1: 2, y1: 3, x2: 3, y2: 3 },
140
145
  ]))
141
146
  .addRule(new CellCountPerZoneRule(Color.Light)))
142
147
  });
@@ -1,6 +1,6 @@
1
1
  import { AnyConfig } from '../config.js';
2
2
  import GridData from '../grid.js';
3
- import { RuleState, Position } from '../primitives.js';
3
+ import { RuleState, Position, Mode } from '../primitives.js';
4
4
  import Rule, { SearchVariant } from './rule.js';
5
5
  export default class ForesightRule extends Rule {
6
6
  readonly count: number;
@@ -23,6 +23,7 @@ export default class ForesightRule extends Rule {
23
23
  validateGrid(_grid: GridData): RuleState;
24
24
  get necessaryForCompletion(): boolean;
25
25
  get isSingleton(): boolean;
26
+ modeVariant(mode: Mode): Rule | null;
26
27
  copyWith({ count, regenInterval, startFull, solvePath, }: {
27
28
  count?: number;
28
29
  regenInterval?: number;
@@ -1,6 +1,6 @@
1
1
  import { ConfigType } from '../config.js';
2
2
  import GridData from '../grid.js';
3
- import { State } from '../primitives.js';
3
+ import { State, Mode } from '../primitives.js';
4
4
  import CustomIconSymbol from '../symbols/customIconSymbol.js';
5
5
  import Rule from './rule.js';
6
6
  class ForesightRule extends Rule {
@@ -65,6 +65,13 @@ class ForesightRule extends Rule {
65
65
  get isSingleton() {
66
66
  return true;
67
67
  }
68
+ modeVariant(mode) {
69
+ // foresight is disabled in perfection mode
70
+ if (mode === Mode.Perfection) {
71
+ return null;
72
+ }
73
+ return this;
74
+ }
68
75
  copyWith({ count, regenInterval, startFull, solvePath, }) {
69
76
  return new ForesightRule(count ?? this.count, regenInterval ?? this.regenInterval, startFull ?? this.startFull, solvePath ?? this.solvePath);
70
77
  }
@@ -49,7 +49,7 @@ Object.defineProperty(Row, "CONFIGS", {
49
49
  },
50
50
  {
51
51
  type: ConfigType.NullableNumber,
52
- default: 0.6,
52
+ default: 0.5,
53
53
  min: 0,
54
54
  max: 1,
55
55
  step: 0.2,
@@ -9,6 +9,7 @@ import Rule, { SearchVariant } from './rule.js';
9
9
  export default class MusicGridRule extends Rule implements GridChangeHandler, SetGridHandler, GridResizeHandler {
10
10
  readonly controlLines: readonly ControlLine[];
11
11
  readonly track: GridData | null;
12
+ readonly normalizeVelocity: boolean;
12
13
  private static readonly EXAMPLE_GRID;
13
14
  private static readonly CONFIGS;
14
15
  private static readonly SEARCH_VARIANTS;
@@ -16,8 +17,9 @@ export default class MusicGridRule extends Rule implements GridChangeHandler, Se
16
17
  * **Music Grid: Listen to the solution**
17
18
  * @param controlLines Denote changes in the playback settings. At least one control line at column 0 should be present to enable playback.
18
19
  * @param track The grid to be played when "listen" is clicked. Set as null to play the solution.
20
+ * @param normalizeVelocity Whether to normalize the velocity of the notes by their pitch such that lower notes are played softer.
19
21
  */
20
- constructor(controlLines: readonly ControlLine[], track: GridData | null);
22
+ constructor(controlLines: readonly ControlLine[], track: GridData | null, normalizeVelocity?: boolean);
21
23
  get id(): string;
22
24
  get explanation(): string;
23
25
  get configs(): readonly AnyConfig[] | null;
@@ -34,9 +36,10 @@ export default class MusicGridRule extends Rule implements GridChangeHandler, Se
34
36
  */
35
37
  setControlLine(controlLine: ControlLine): this;
36
38
  withTrack(track: GridData | null): this;
37
- copyWith({ controlLines, track, }: {
39
+ copyWith({ controlLines, track, normalizeVelocity, }: {
38
40
  controlLines?: readonly ControlLine[];
39
41
  track?: GridData | null;
42
+ normalizeVelocity?: boolean;
40
43
  }): this;
41
44
  get validateWithSolution(): boolean;
42
45
  get isSingleton(): boolean;
@@ -6,22 +6,23 @@ import CustomIconSymbol from '../symbols/customIconSymbol.js';
6
6
  import { ControlLine, Row } from './musicControlLine.js';
7
7
  import Rule from './rule.js';
8
8
  const DEFAULT_SCALLE = [
9
- new Row('C5', 0.6),
10
- new Row('B4', 0.6),
11
- new Row('A4', 0.6),
12
- new Row('G4', 0.6),
13
- new Row('F4', 0.6),
14
- new Row('E4', 0.6),
15
- new Row('D4', 0.6),
16
- new Row('C4', 0.6),
9
+ new Row('C5', null),
10
+ new Row('B4', null),
11
+ new Row('A4', null),
12
+ new Row('G4', null),
13
+ new Row('F4', null),
14
+ new Row('E4', null),
15
+ new Row('D4', null),
16
+ new Row('C4', null),
17
17
  ];
18
18
  class MusicGridRule extends Rule {
19
19
  /**
20
20
  * **Music Grid: Listen to the solution**
21
21
  * @param controlLines Denote changes in the playback settings. At least one control line at column 0 should be present to enable playback.
22
22
  * @param track The grid to be played when "listen" is clicked. Set as null to play the solution.
23
+ * @param normalizeVelocity Whether to normalize the velocity of the notes by their pitch such that lower notes are played softer.
23
24
  */
24
- constructor(controlLines, track) {
25
+ constructor(controlLines, track, normalizeVelocity = true) {
25
26
  super();
26
27
  Object.defineProperty(this, "controlLines", {
27
28
  enumerable: true,
@@ -35,8 +36,15 @@ class MusicGridRule extends Rule {
35
36
  writable: true,
36
37
  value: track
37
38
  });
39
+ Object.defineProperty(this, "normalizeVelocity", {
40
+ enumerable: true,
41
+ configurable: true,
42
+ writable: true,
43
+ value: normalizeVelocity
44
+ });
38
45
  this.controlLines = MusicGridRule.deduplicateControlLines(controlLines);
39
46
  this.track = track;
47
+ this.normalizeVelocity = normalizeVelocity;
40
48
  }
41
49
  get id() {
42
50
  return MajorRule.MusicGrid;
@@ -134,8 +142,8 @@ class MusicGridRule extends Rule {
134
142
  withTrack(track) {
135
143
  return this.copyWith({ track });
136
144
  }
137
- copyWith({ controlLines, track, }) {
138
- return new MusicGridRule(controlLines ?? this.controlLines, track !== undefined ? track : this.track);
145
+ copyWith({ controlLines, track, normalizeVelocity, }) {
146
+ return new MusicGridRule(controlLines ?? this.controlLines, track !== undefined ? track : this.track, normalizeVelocity ?? this.normalizeVelocity);
139
147
  }
140
148
  get validateWithSolution() {
141
149
  return true;
@@ -197,6 +205,13 @@ Object.defineProperty(MusicGridRule, "CONFIGS", {
197
205
  description: 'Track',
198
206
  configurable: true,
199
207
  },
208
+ {
209
+ type: ConfigType.Boolean,
210
+ default: true,
211
+ field: 'normalizeVelocity',
212
+ description: 'Normalize Velocity',
213
+ configurable: true,
214
+ },
200
215
  ])
201
216
  });
202
217
  Object.defineProperty(MusicGridRule, "SEARCH_VARIANTS", {
@@ -1,16 +1,19 @@
1
1
  import { AnyConfig } from '../config.js';
2
2
  import GridData from '../grid.js';
3
- import { GridState, RuleState } from '../primitives.js';
3
+ import { GridState, RuleState, Mode } from '../primitives.js';
4
4
  import Rule, { SearchVariant } from './rule.js';
5
5
  import { SetGridHandler } from '../events/onSetGrid.js';
6
6
  import { FinalValidationHandler } from '../events/onFinalValidation.js';
7
7
  export default class PerfectionRule extends Rule implements SetGridHandler, FinalValidationHandler {
8
+ readonly editor: boolean;
8
9
  private static readonly EXAMPLE_GRID;
9
10
  private static readonly SEARCH_VARIANTS;
10
11
  /**
11
12
  * **Quest for Perfection: cell colors are final**
13
+ *
14
+ * @param editor - whether to enable editor mode. This field is automatically set by the editor.
12
15
  */
13
- constructor();
16
+ constructor(editor?: boolean);
14
17
  get id(): string;
15
18
  get explanation(): string;
16
19
  get configs(): readonly AnyConfig[] | null;
@@ -18,18 +21,23 @@ export default class PerfectionRule extends Rule implements SetGridHandler, Fina
18
21
  get searchVariants(): SearchVariant[];
19
22
  get necessaryForCompletion(): boolean;
20
23
  get isSingleton(): boolean;
24
+ modeVariant(mode: Mode): Rule | null;
21
25
  validateGrid(grid: GridData): RuleState;
22
26
  /**
23
27
  * If the grid passes validation but is different from the solution, indicate the error in the final state.
24
28
  */
25
29
  onFinalValidation(grid: GridData, solution: GridData | null, state: GridState): GridState;
26
30
  private fixTiles;
31
+ private isValid;
32
+ private findSingleError;
27
33
  /**
28
34
  * Force all tiles to be fixed.
29
35
  *
30
36
  * If the grid is already wrong, prevent the player from changing it further.
31
37
  */
32
38
  onSetGrid(oldGrid: GridData, newGrid: GridData, solution: GridData | null): GridData;
33
- copyWith(_: object): this;
39
+ copyWith({ editor }: {
40
+ editor?: boolean;
41
+ }): this;
34
42
  }
35
43
  export declare const instance: PerfectionRule;