@logic-pad/core 0.17.1 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
  /**
@@ -3083,9 +3065,10 @@ declare global {
3083
3065
  }): this;
3084
3066
  withRevealLocation(revealLocation: boolean): this;
3085
3067
  }
3086
- export declare class HouseSymbol extends Symbol$1 {
3068
+ export declare class HouseSymbol extends NumberSymbol {
3087
3069
  readonly x: number;
3088
3070
  readonly y: number;
3071
+ readonly number: number;
3089
3072
  readonly title = 'House';
3090
3073
  private static readonly CONFIGS;
3091
3074
  private static readonly EXAMPLE_GRID;
@@ -3094,14 +3077,26 @@ declare global {
3094
3077
  *
3095
3078
  * @param x - The x-coordinate of the symbol.
3096
3079
  * @param y - The y-coordinate of the symbol.
3080
+ * @param number - The number of houses in this region.
3097
3081
  */
3098
- constructor(x: number, y: number);
3082
+ constructor(x: number, y: number, number: number);
3099
3083
  get id(): string;
3100
3084
  get explanation(): string;
3101
3085
  get configs(): readonly AnyConfig[] | null;
3102
3086
  createExampleGrid(): GridData;
3103
- validateSymbol(grid: GridData): State;
3104
- copyWith({ x, y }: { x?: number; y?: number }): this;
3087
+ countTiles(grid: GridData): {
3088
+ completed: number;
3089
+ possible: number;
3090
+ };
3091
+ copyWith({
3092
+ x,
3093
+ y,
3094
+ number,
3095
+ }: {
3096
+ x?: number;
3097
+ y?: number;
3098
+ number?: number;
3099
+ }): this;
3105
3100
  }
3106
3101
  export declare const allSymbols: Map<string, Symbol$1>;
3107
3102
  export declare function aggregateState(
@@ -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)) {
@@ -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;
@@ -1,10 +1,10 @@
1
1
  import { AnyConfig } from '../config.js';
2
2
  import GridData from '../grid.js';
3
- import { State } from '../primitives.js';
4
- import Symbol from './symbol.js';
5
- export default class HouseSymbol extends Symbol {
3
+ import NumberSymbol from './numberSymbol.js';
4
+ export default class HouseSymbol extends NumberSymbol {
6
5
  readonly x: number;
7
6
  readonly y: number;
7
+ readonly number: number;
8
8
  readonly title = "House";
9
9
  private static readonly CONFIGS;
10
10
  private static readonly EXAMPLE_GRID;
@@ -13,16 +13,21 @@ export default class HouseSymbol extends Symbol {
13
13
  *
14
14
  * @param x - The x-coordinate of the symbol.
15
15
  * @param y - The y-coordinate of the symbol.
16
+ * @param number - The number of houses in this region.
16
17
  */
17
- constructor(x: number, y: number);
18
+ constructor(x: number, y: number, number: number);
18
19
  get id(): string;
19
20
  get explanation(): string;
20
21
  get configs(): readonly AnyConfig[] | null;
21
22
  createExampleGrid(): GridData;
22
- validateSymbol(grid: GridData): State;
23
- copyWith({ x, y }: {
23
+ countTiles(grid: GridData): {
24
+ completed: number;
25
+ possible: number;
26
+ };
27
+ copyWith({ x, y, number, }: {
24
28
  x?: number;
25
29
  y?: number;
30
+ number?: number;
26
31
  }): this;
27
32
  }
28
33
  export declare const instance: HouseSymbol;
@@ -1,17 +1,18 @@
1
1
  import { ConfigType } from '../config.js';
2
2
  import GridData from '../grid.js';
3
3
  import { array } from '../dataHelper.js';
4
- import { Color, State } from '../primitives.js';
5
- import Symbol from './symbol.js';
6
- class HouseSymbol extends Symbol {
4
+ import { Color } from '../primitives.js';
5
+ import NumberSymbol from './numberSymbol.js';
6
+ class HouseSymbol extends NumberSymbol {
7
7
  /**
8
8
  * **Houses must connect to exactly one other house**
9
9
  *
10
10
  * @param x - The x-coordinate of the symbol.
11
11
  * @param y - The y-coordinate of the symbol.
12
+ * @param number - The number of houses in this region.
12
13
  */
13
- constructor(x, y) {
14
- super(x, y);
14
+ constructor(x, y, number) {
15
+ super(x, y, number);
15
16
  Object.defineProperty(this, "x", {
16
17
  enumerable: true,
17
18
  configurable: true,
@@ -24,6 +25,12 @@ class HouseSymbol extends Symbol {
24
25
  writable: true,
25
26
  value: y
26
27
  });
28
+ Object.defineProperty(this, "number", {
29
+ enumerable: true,
30
+ configurable: true,
31
+ writable: true,
32
+ value: number
33
+ });
27
34
  Object.defineProperty(this, "title", {
28
35
  enumerable: true,
29
36
  configurable: true,
@@ -35,7 +42,7 @@ class HouseSymbol extends Symbol {
35
42
  return `house`;
36
43
  }
37
44
  get explanation() {
38
- return '*Houses* must connect to *exactly one* other house';
45
+ return '*House numbers* count the number of houses in the region';
39
46
  }
40
47
  get configs() {
41
48
  return HouseSymbol.CONFIGS;
@@ -43,45 +50,39 @@ class HouseSymbol extends Symbol {
43
50
  createExampleGrid() {
44
51
  return HouseSymbol.EXAMPLE_GRID;
45
52
  }
46
- validateSymbol(grid) {
53
+ countTiles(grid) {
47
54
  const thisX = Math.floor(this.x);
48
55
  const thisY = Math.floor(this.y);
49
- let complete = true;
50
56
  const visited = array(grid.width, grid.height, () => false);
51
57
  const connected = array(grid.width, grid.height, () => false);
52
58
  const color = grid.getTile(thisX, thisY).color;
53
- grid.iterateArea({ x: thisX, y: thisY }, tile => color === Color.Gray ||
54
- tile.color === Color.Gray ||
55
- tile.color === color, (tile, x, y) => {
59
+ if (color === Color.Gray)
60
+ return { completed: 0, possible: Number.MAX_SAFE_INTEGER };
61
+ grid.iterateArea({ x: thisX, y: thisY }, tile => tile.color === Color.Gray || tile.color === color, (_, x, y) => {
56
62
  visited[y][x] = true;
57
- if (tile.color === Color.Gray)
58
- complete = false;
59
63
  });
60
64
  grid.iterateArea({ x: thisX, y: thisY }, tile => tile.color === color, (_, x, y) => {
61
65
  connected[y][x] = true;
62
66
  });
63
- let connectedHouses = 0;
67
+ let completedHouses = 0;
64
68
  let possibleHouses = 0;
65
69
  for (const symbol of grid.symbols.get(this.id) ?? []) {
66
- if (symbol !== this && symbol instanceof HouseSymbol) {
70
+ if (symbol instanceof HouseSymbol) {
67
71
  const symbolX = Math.floor(symbol.x);
68
72
  const symbolY = Math.floor(symbol.y);
69
73
  if (connected[symbolY][symbolX])
70
- connectedHouses++;
74
+ completedHouses++;
71
75
  else if (visited[symbolY][symbolX])
72
76
  possibleHouses++;
73
77
  }
74
78
  }
75
- if (color !== Color.Gray &&
76
- (connectedHouses > 1 || connectedHouses + possibleHouses < 1)) {
77
- return State.Error;
78
- }
79
- return complete || (connectedHouses === 1 && possibleHouses === 0)
80
- ? State.Satisfied
81
- : State.Incomplete;
79
+ return {
80
+ completed: completedHouses,
81
+ possible: completedHouses + possibleHouses,
82
+ };
82
83
  }
83
- copyWith({ x, y }) {
84
- return new HouseSymbol(x ?? this.x, y ?? this.y);
84
+ copyWith({ x, y, number, }) {
85
+ return new HouseSymbol(x ?? this.x, y ?? this.y, number ?? this.number);
85
86
  }
86
87
  }
87
88
  Object.defineProperty(HouseSymbol, "CONFIGS", {
@@ -103,6 +104,14 @@ Object.defineProperty(HouseSymbol, "CONFIGS", {
103
104
  description: 'Y',
104
105
  configurable: false,
105
106
  },
107
+ {
108
+ type: ConfigType.Number,
109
+ default: 2,
110
+ field: 'number',
111
+ description: 'Number',
112
+ explanation: 'Number of houses in this region',
113
+ configurable: true,
114
+ },
106
115
  ])
107
116
  });
108
117
  Object.defineProperty(HouseSymbol, "EXAMPLE_GRID", {
@@ -110,12 +119,12 @@ Object.defineProperty(HouseSymbol, "EXAMPLE_GRID", {
110
119
  configurable: true,
111
120
  writable: true,
112
121
  value: Object.freeze(GridData.create(['bbbww', 'wwwbw', 'wbbbw', 'wwwww'])
113
- .addSymbol(new HouseSymbol(0, 0))
114
- .addSymbol(new HouseSymbol(2, 0))
115
- .addSymbol(new HouseSymbol(3, 0))
116
- .addSymbol(new HouseSymbol(2, 1))
117
- .addSymbol(new HouseSymbol(3, 1))
118
- .addSymbol(new HouseSymbol(1, 2)))
122
+ .addSymbol(new HouseSymbol(0, 0, 2))
123
+ .addSymbol(new HouseSymbol(2, 0, 2))
124
+ .addSymbol(new HouseSymbol(3, 0, 2))
125
+ .addSymbol(new HouseSymbol(2, 1, 2))
126
+ .addSymbol(new HouseSymbol(3, 1, 2))
127
+ .addSymbol(new HouseSymbol(1, 2, 2)))
119
128
  });
120
129
  export default HouseSymbol;
121
- export const instance = new HouseSymbol(0, 0);
130
+ export const instance = new HouseSymbol(0, 0, 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.17.1",
3
+ "version": "0.19.0",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",