@logic-pad/core 0.18.0 → 0.19.1

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.
@@ -773,54 +773,36 @@ declare global {
773
773
  connections?: GridConnections,
774
774
  zones?: GridZones,
775
775
  symbols?: ReadonlyMap<string, readonly Symbol$1[]>,
776
- rules?: readonly Rule[]
776
+ rules?: readonly Rule[],
777
+ sanitize?: boolean,
778
+ triggerEvents?: boolean
777
779
  ): GridData;
778
780
  /**
779
781
  * Copy the current grid while modifying the provided properties.
780
782
  * @param param0 The properties to modify.
781
783
  * @returns The new grid with the modified properties.
782
784
  */
783
- copyWith({
784
- width,
785
- height,
786
- tiles,
787
- connections,
788
- zones,
789
- symbols,
790
- rules,
791
- }: {
792
- width?: number;
793
- height?: number;
794
- tiles?: readonly (readonly TileData[])[];
795
- connections?: GridConnections;
796
- zones?: GridZones;
797
- symbols?: ReadonlyMap<string, readonly Symbol$1[]>;
798
- rules?: readonly Rule[];
799
- }): GridData;
800
- /**
801
- * Copy the current grid while modifying the provided properties.
802
- * Skip sanitization and event triggering for performance.
803
- *
804
- * @param param0 The properties to modify.
805
- * @returns The new grid with the modified properties.
806
- */
807
- fastCopyWith({
808
- width,
809
- height,
810
- tiles,
811
- connections,
812
- zones,
813
- symbols,
814
- rules,
815
- }: {
816
- width?: number;
817
- height?: number;
818
- tiles?: readonly (readonly TileData[])[];
819
- connections?: GridConnections;
820
- zones?: GridZones;
821
- symbols?: ReadonlyMap<string, readonly Symbol$1[]>;
822
- rules?: readonly Rule[];
823
- }): GridData;
785
+ copyWith(
786
+ {
787
+ width,
788
+ height,
789
+ tiles,
790
+ connections,
791
+ zones,
792
+ symbols,
793
+ rules,
794
+ }: {
795
+ width?: number;
796
+ height?: number;
797
+ tiles?: readonly (readonly TileData[])[];
798
+ connections?: GridConnections;
799
+ zones?: GridZones;
800
+ symbols?: ReadonlyMap<string, readonly Symbol$1[]>;
801
+ rules?: readonly Rule[];
802
+ },
803
+ sanitize?: boolean,
804
+ triggerEvents?: boolean
805
+ ): GridData;
824
806
  toArrayCoordinates(x: number, y: number): Position$1;
825
807
  isPositionValid(x: number, y: number): boolean;
826
808
  /**
@@ -57,7 +57,7 @@ export default class GridData {
57
57
  * @param symbols The symbols in the grid.
58
58
  * @param rules The rules of the grid.
59
59
  */
60
- static create(width: number, height: number, tiles?: readonly (readonly TileData[])[], connections?: GridConnections, zones?: GridZones, symbols?: ReadonlyMap<string, readonly Symbol[]>, rules?: readonly Rule[]): GridData;
60
+ static create(width: number, height: number, tiles?: readonly (readonly TileData[])[], connections?: GridConnections, zones?: GridZones, symbols?: ReadonlyMap<string, readonly Symbol[]>, rules?: readonly Rule[], sanitize?: boolean, triggerEvents?: boolean): GridData;
61
61
  /**
62
62
  * Copy the current grid while modifying the provided properties.
63
63
  * @param param0 The properties to modify.
@@ -71,23 +71,7 @@ export default class GridData {
71
71
  zones?: GridZones;
72
72
  symbols?: ReadonlyMap<string, readonly Symbol[]>;
73
73
  rules?: readonly Rule[];
74
- }): GridData;
75
- /**
76
- * Copy the current grid while modifying the provided properties.
77
- * Skip sanitization and event triggering for performance.
78
- *
79
- * @param param0 The properties to modify.
80
- * @returns The new grid with the modified properties.
81
- */
82
- fastCopyWith({ width, height, tiles, connections, zones, symbols, rules, }: {
83
- width?: number;
84
- height?: number;
85
- tiles?: readonly (readonly TileData[])[];
86
- connections?: GridConnections;
87
- zones?: GridZones;
88
- symbols?: ReadonlyMap<string, readonly Symbol[]>;
89
- rules?: readonly Rule[];
90
- }): GridData;
74
+ }, sanitize?: boolean, triggerEvents?: boolean): GridData;
91
75
  toArrayCoordinates(x: number, y: number): Position;
92
76
  isPositionValid(x: number, y: number): boolean;
93
77
  /**
package/dist/data/grid.js CHANGED
@@ -102,28 +102,62 @@ export default class GridData {
102
102
  this.symbols = symbols ?? new Map();
103
103
  this.rules = rules ?? [];
104
104
  }
105
- static create(arrayOrWidth, height, tiles, connections, zones, symbols, rules) {
105
+ static create(arrayOrWidth, height, tiles, connections, zones, symbols, rules, sanitize, triggerEvents) {
106
106
  if (typeof arrayOrWidth === 'number') {
107
+ let hasGridChangeSymbols = false;
108
+ let hasGridChangeRules = false;
109
+ if (triggerEvents) {
110
+ symbols?.forEach(list => {
111
+ list.forEach(sym => {
112
+ if (handlesGridChange(sym)) {
113
+ hasGridChangeSymbols = true;
114
+ }
115
+ });
116
+ });
117
+ rules?.forEach(rule => {
118
+ if (handlesGridChange(rule)) {
119
+ hasGridChangeRules = true;
120
+ }
121
+ });
122
+ }
107
123
  const newSymbols = symbols
108
- ? GridData.deduplicateSymbols(symbols)
124
+ ? sanitize
125
+ ? GridData.deduplicateSymbols(symbols)
126
+ : triggerEvents && hasGridChangeSymbols
127
+ ? new Map([...symbols.entries()].map(([id, list]) => [id, list.slice()]))
128
+ : symbols
109
129
  : new Map();
110
130
  // do not deduplicate all rules because it makes for bad editor experience
111
- const newRules = rules ? GridData.deduplicateSingletonRules(rules) : [];
131
+ const newRules = rules
132
+ ? sanitize
133
+ ? GridData.deduplicateSingletonRules(rules)
134
+ : triggerEvents && hasGridChangeRules
135
+ ? rules.slice()
136
+ : rules
137
+ : [];
112
138
  const newGrid = new GridData(arrayOrWidth, height, tiles, connections
113
- ? GridConnections.validateEdges(connections, arrayOrWidth, height)
114
- : connections, zones ? GridZones.validateEdges(zones, arrayOrWidth, height) : zones, newSymbols, newRules);
115
- newSymbols.forEach(list => {
116
- list.forEach((sym, i) => {
117
- if (handlesGridChange(sym)) {
118
- list[i] = sym.onGridChange(newGrid);
139
+ ? sanitize
140
+ ? GridConnections.validateEdges(connections, arrayOrWidth, height)
141
+ : connections
142
+ : undefined, zones
143
+ ? sanitize
144
+ ? GridZones.validateEdges(zones, arrayOrWidth, height)
145
+ : zones
146
+ : undefined, newSymbols, newRules);
147
+ if (triggerEvents) {
148
+ newSymbols.forEach(list => {
149
+ list.forEach((sym, i) => {
150
+ if (handlesGridChange(sym)) {
151
+ list[i] = sym.onGridChange(newGrid);
152
+ }
153
+ });
154
+ });
155
+ newRules.forEach((rule, i) => {
156
+ if (handlesGridChange(rule)) {
157
+ newRules[i] = rule.onGridChange(newGrid);
119
158
  }
120
159
  });
121
- });
122
- newRules.forEach((rule, i) => {
123
- if (handlesGridChange(rule)) {
124
- newRules[i] = rule.onGridChange(newGrid);
125
- }
126
- });
160
+ }
127
161
  return newGrid;
128
162
  }
129
163
  else {
@@ -136,18 +170,8 @@ export default class GridData {
136
170
  * @param param0 The properties to modify.
137
171
  * @returns The new grid with the modified properties.
138
172
  */
139
- copyWith({ width, height, tiles, connections, zones, symbols, rules, }) {
140
- return GridData.create(width ?? this.width, height ?? this.height, tiles ?? this.tiles, connections ?? this.connections, zones ?? this.zones, symbols ?? this.symbols, rules ?? this.rules);
141
- }
142
- /**
143
- * Copy the current grid while modifying the provided properties.
144
- * Skip sanitization and event triggering for performance.
145
- *
146
- * @param param0 The properties to modify.
147
- * @returns The new grid with the modified properties.
148
- */
149
- fastCopyWith({ width, height, tiles, connections, zones, symbols, rules, }) {
150
- return new GridData(width ?? this.width, height ?? this.height, tiles ?? this.tiles, connections ?? this.connections, zones ?? this.zones, symbols ?? this.symbols, rules ?? this.rules);
173
+ copyWith({ width, height, tiles, connections, zones, symbols, rules, }, sanitize = true, triggerEvents = true) {
174
+ return GridData.create(width ?? this.width, height ?? this.height, tiles ?? this.tiles, connections ?? this.connections, zones ?? this.zones, symbols ?? this.symbols, rules ?? this.rules, sanitize, triggerEvents);
151
175
  }
152
176
  toArrayCoordinates(x, y) {
153
177
  // // This is the preferred way to compute tile coordinates, but for performance reasons we will just access the
@@ -733,7 +757,7 @@ export default class GridData {
733
757
  this.iterateArea(position, t => t.color === from && (allowFixed || !t.fixed), (tile, x, y) => {
734
758
  tiles[y][x] = tile.withColor(to);
735
759
  });
736
- return this.copyWith({ tiles });
760
+ return this.copyWith({ tiles }, false);
737
761
  }
738
762
  /**
739
763
  * Flood fill all tiles with the given color to a new color, even if they are not connected.
@@ -748,7 +772,7 @@ export default class GridData {
748
772
  tiles: this.tiles.map(row => row.map(tile => tile.color === from && (allowFixed || !tile.fixed)
749
773
  ? tile.withColor(to)
750
774
  : tile)),
751
- });
775
+ }, false);
752
776
  }
753
777
  /**
754
778
  * Check if the grid has any instructions that require a custom solution.
@@ -778,7 +802,7 @@ export default class GridData {
778
802
  });
779
803
  if (!changed)
780
804
  return this;
781
- let newGrid = this.copyWith({ tiles: newTiles });
805
+ let newGrid = this.copyWith({ tiles: newTiles }, false);
782
806
  this.symbols.forEach(list => {
783
807
  list.forEach(symbol => {
784
808
  if (handlesSetGrid(symbol)) {
@@ -1,24 +1,24 @@
1
1
  import { z } from 'zod';
2
2
  import GridData from './grid.js';
3
3
  import { Color, PuzzleType, State } from './primitives.js';
4
- export const MetadataSchema = z
5
- .object({
6
- title: z.string().min(1),
7
- author: z.string().min(1),
8
- description: z.string(),
9
- difficulty: z.number().int().min(0).max(10),
10
- })
11
- .strict();
12
- export const PuzzleSchema = z
13
- .object({
14
- title: z.string().min(1),
15
- author: z.string().min(1),
16
- description: z.string(),
17
- difficulty: z.number().int().min(0).max(10),
18
- grid: z.instanceof(GridData),
19
- solution: z.instanceof(GridData).nullable(),
20
- })
21
- .strict();
4
+ export const MetadataSchema = z.strictObject({
5
+ title: z.string('Title must be a string').min(1, 'Title must not be empty'),
6
+ author: z
7
+ .string('Author must be a string')
8
+ .min(1, 'Author must not be empty'),
9
+ description: z.string('Description must be a string'),
10
+ difficulty: z
11
+ .number('Difficulty must be a number')
12
+ .int('Difficulty must be an integer')
13
+ .min(0, 'Difficulty must be at least 0')
14
+ .max(10, 'Difficulty must be at most 10'),
15
+ }, 'Data must be an object');
16
+ export const PuzzleSchema = MetadataSchema.extend({
17
+ grid: z.instanceof(GridData, { error: 'Grid must be a GridData instance' }),
18
+ solution: z
19
+ .instanceof(GridData, { error: 'Solution must be a GridData instance' })
20
+ .nullable(),
21
+ }).strict();
22
22
  /**
23
23
  * Checks if two puzzles are equal.
24
24
  */
@@ -231,9 +231,9 @@ function solveUnderclued(input) {
231
231
  function search(x, y, tile, color) {
232
232
  // count++;
233
233
  // console.log(`Trying (${x}, ${y}) with ${color}`);
234
- const newGrid = grid.fastCopyWith({
234
+ const newGrid = grid.copyWith({
235
235
  tiles: grid.setTile(x, y, tile.withColor(color)),
236
- });
236
+ }, false, false);
237
237
  // Solve
238
238
  let solution;
239
239
  solveNormal(newGrid, sol => {
@@ -265,13 +265,13 @@ function solveUnderclued(input) {
265
265
  if (!darkPossible && !lightPossible)
266
266
  return null;
267
267
  if (darkPossible && !lightPossible)
268
- grid = grid.fastCopyWith({
268
+ grid = grid.copyWith({
269
269
  tiles: grid.setTile(x, y, tile.withColor(Color.Dark)),
270
- });
270
+ }, false, false);
271
271
  if (!darkPossible && lightPossible)
272
- grid = grid.fastCopyWith({
272
+ grid = grid.copyWith({
273
273
  tiles: grid.setTile(x, y, tile.withColor(Color.Light)),
274
- });
274
+ }, false, false);
275
275
  }
276
276
  }
277
277
  // console.log(`Solve count: ${count}`);
@@ -7,9 +7,9 @@ function gridToRawTiles(grid) {
7
7
  return array(grid.width, grid.height, (x, y) => grid.getTile(x, y).color);
8
8
  }
9
9
  function rawTilesToGrid(rawTiles, grid) {
10
- return grid.fastCopyWith({
10
+ return grid.copyWith({
11
11
  tiles: array(grid.width, grid.height, (x, y) => grid.getTile(x, y).withColor(rawTiles[y][x])),
12
- });
12
+ }, false, false);
13
13
  }
14
14
  function getNextTile(grid, rawTiles) {
15
15
  for (let y = 0; y < grid.height; y++) {
@@ -58,9 +58,9 @@ function solveUnderclued(input) {
58
58
  light: false,
59
59
  }));
60
60
  function search(x, y, tile, color) {
61
- const newGrid = grid.fastCopyWith({
61
+ const newGrid = grid.copyWith({
62
62
  tiles: grid.setTile(x, y, tile.withColor(color)),
63
- });
63
+ }, false, false);
64
64
  // Solve
65
65
  let solution;
66
66
  solveNormal(newGrid, sol => {
@@ -92,13 +92,13 @@ function solveUnderclued(input) {
92
92
  if (!darkPossible && !lightPossible)
93
93
  return null;
94
94
  if (darkPossible && !lightPossible)
95
- grid = grid.fastCopyWith({
95
+ grid = grid.copyWith({
96
96
  tiles: grid.setTile(x, y, tile.withColor(Color.Dark)),
97
- });
97
+ }, false, false);
98
98
  if (!darkPossible && lightPossible)
99
- grid = grid.fastCopyWith({
99
+ grid = grid.copyWith({
100
100
  tiles: grid.setTile(x, y, tile.withColor(Color.Light)),
101
- });
101
+ }, false, false);
102
102
  }
103
103
  }
104
104
  return grid;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.18.0",
3
+ "version": "0.19.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",