@logic-pad/core 0.21.0 → 0.22.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.
@@ -1036,7 +1036,11 @@ declare global {
1036
1036
  */
1037
1037
  iterateArea<T>(
1038
1038
  position: Position$1,
1039
- predicate: (tile: TileData) => boolean,
1039
+ predicate: (
1040
+ tile: TileData,
1041
+ logicalX: number,
1042
+ logicalY: number
1043
+ ) => boolean,
1040
1044
  callback: (
1041
1045
  tile: TileData,
1042
1046
  x: number,
@@ -1061,7 +1065,11 @@ declare global {
1061
1065
  iterateDirection<T>(
1062
1066
  position: Position$1,
1063
1067
  direction: Direction | Orientation,
1064
- predicate: (tile: TileData) => boolean,
1068
+ predicate: (
1069
+ tile: TileData,
1070
+ logicalX: number,
1071
+ logicalY: number
1072
+ ) => boolean,
1065
1073
  callback: (
1066
1074
  tile: TileData,
1067
1075
  x: number,
@@ -1086,7 +1094,11 @@ declare global {
1086
1094
  iterateDirectionAll<T>(
1087
1095
  position: Position$1,
1088
1096
  direction: Direction | Orientation,
1089
- predicate: (tile: TileData) => boolean,
1097
+ predicate: (
1098
+ tile: TileData,
1099
+ logicalX: number,
1100
+ logicalY: number
1101
+ ) => boolean,
1090
1102
  callback: (
1091
1103
  tile: TileData,
1092
1104
  x: number,
@@ -1096,6 +1108,26 @@ declare global {
1096
1108
  ) => T | undefined,
1097
1109
  visited?: boolean[][]
1098
1110
  ): T | undefined;
1111
+ /**
1112
+ * Reduce the grid by zones defined in the GridZones.
1113
+ *
1114
+ * @param reducer The reducer function to apply to each zone.
1115
+ * @param initializer The initializer function to create the initial value for each zone.
1116
+ * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
1117
+ * @returns An array of reduced values, one for each zone.
1118
+ */
1119
+ reduceByZone<T>(
1120
+ reducer: (
1121
+ acc: T,
1122
+ tile: TileData,
1123
+ x: number,
1124
+ y: number,
1125
+ logicalX: number,
1126
+ logicalY: number
1127
+ ) => T,
1128
+ initializer: () => T,
1129
+ visited?: boolean[][]
1130
+ ): T[];
1099
1131
  /**
1100
1132
  * Check if every tile in the grid is filled with a color other than gray.
1101
1133
  *
@@ -1628,6 +1660,28 @@ declare global {
1628
1660
  copyWith({ color }: { color?: Color }): this;
1629
1661
  withColor(color: Color): this;
1630
1662
  }
1663
+ export declare class ConnectZonesRule extends Rule {
1664
+ readonly color: Color;
1665
+ readonly title = 'Connect Zones';
1666
+ private static readonly CONFIGS;
1667
+ private static readonly EXAMPLE_GRID_LIGHT;
1668
+ private static readonly EXAMPLE_GRID_DARK;
1669
+ private static readonly SEARCH_VARIANTS;
1670
+ /**
1671
+ * **Connect all &lt;color&gt; cells in each zone**
1672
+ *
1673
+ * @param color - The color of the cells to connect.
1674
+ */
1675
+ constructor(color: Color);
1676
+ get id(): string;
1677
+ get explanation(): string;
1678
+ get configs(): readonly AnyConfig[] | null;
1679
+ createExampleGrid(): GridData;
1680
+ get searchVariants(): SearchVariant[];
1681
+ validateGrid(grid: GridData): RuleState;
1682
+ copyWith({ color }: { color?: Color }): this;
1683
+ withColor(color: Color): this;
1684
+ }
1631
1685
  export type ShapeRegions = {
1632
1686
  regions: {
1633
1687
  positions: Position$1[];
@@ -1723,6 +1777,30 @@ declare global {
1723
1777
  validateGrid(grid: GridData): RuleState;
1724
1778
  copyWith({ color }: { color?: Color }): this;
1725
1779
  }
1780
+ export declare class ExactCountPerZoneRule extends CellCountPerZoneRule {
1781
+ readonly color: Color;
1782
+ readonly count: number;
1783
+ readonly title = 'Exact Count Per Zone';
1784
+ private static readonly CONFIGS;
1785
+ private static readonly EXAMPLE_GRID_LIGHT;
1786
+ private static readonly EXAMPLE_GRID_DARK;
1787
+ private static readonly EXAMPLE_GRID_GRAY;
1788
+ private static readonly SEARCH_VARIANTS;
1789
+ /**
1790
+ * **Each zone has &lt;count&gt; &lt;color&gt; cells.**
1791
+ *
1792
+ * @param color - The color of the cells to count.
1793
+ * @param count - The exact count of the cells in each zone.
1794
+ */
1795
+ constructor(color: Color, count: number);
1796
+ get id(): string;
1797
+ get explanation(): string;
1798
+ get configs(): readonly AnyConfig[] | null;
1799
+ createExampleGrid(): GridData;
1800
+ get searchVariants(): SearchVariant[];
1801
+ validateGrid(grid: GridData): RuleState;
1802
+ copyWith({ color, count }: { color?: Color; count?: number }): this;
1803
+ }
1726
1804
  export declare class ForesightRule extends Rule {
1727
1805
  readonly count: number;
1728
1806
  readonly regenInterval: number;
@@ -245,7 +245,7 @@ export default class GridData {
245
245
  * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
246
246
  * @returns The value returned by the callback that stopped the iteration, or undefined if the iteration completed.
247
247
  */
248
- iterateArea<T>(position: Position, predicate: (tile: TileData) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => undefined | T, visited?: boolean[][]): T | undefined;
248
+ iterateArea<T>(position: Position, predicate: (tile: TileData, logicalX: number, logicalY: number) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => undefined | T, visited?: boolean[][]): T | undefined;
249
249
  /**
250
250
  * Iterate over all tiles in a straight line from the given position in the given direction that satisfy the predicate.
251
251
  * The iteration stops when the callback returns a value that is not undefined.
@@ -258,7 +258,7 @@ export default class GridData {
258
258
  * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
259
259
  * @returns The value returned by the callback that stopped the iteration, or undefined if the iteration completed.
260
260
  */
261
- iterateDirection<T>(position: Position, direction: Direction | Orientation, predicate: (tile: TileData) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => T | undefined, visited?: boolean[][]): T | undefined;
261
+ iterateDirection<T>(position: Position, direction: Direction | Orientation, predicate: (tile: TileData, logicalX: number, logicalY: number) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => T | undefined, visited?: boolean[][]): T | undefined;
262
262
  /**
263
263
  * Iterate over all tiles in a straight line from the given position in the given direction that satisfy the predicate.
264
264
  * The iteration stops when the callback returns a value that is not undefined.
@@ -271,7 +271,16 @@ export default class GridData {
271
271
  * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
272
272
  * @returns The value returned by the callback that stopped the iteration, or undefined if the iteration completed.
273
273
  */
274
- iterateDirectionAll<T>(position: Position, direction: Direction | Orientation, predicate: (tile: TileData) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => T | undefined, visited?: boolean[][]): T | undefined;
274
+ iterateDirectionAll<T>(position: Position, direction: Direction | Orientation, predicate: (tile: TileData, logicalX: number, logicalY: number) => boolean, callback: (tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => T | undefined, visited?: boolean[][]): T | undefined;
275
+ /**
276
+ * Reduce the grid by zones defined in the GridZones.
277
+ *
278
+ * @param reducer The reducer function to apply to each zone.
279
+ * @param initializer The initializer function to create the initial value for each zone.
280
+ * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
281
+ * @returns An array of reduced values, one for each zone.
282
+ */
283
+ reduceByZone<T>(reducer: (acc: T, tile: TileData, x: number, y: number, logicalX: number, logicalY: number) => T, initializer: () => T, visited?: boolean[][]): T[];
275
284
  /**
276
285
  * Check if every tile in the grid is filled with a color other than gray.
277
286
  *
package/dist/data/grid.js CHANGED
@@ -650,7 +650,7 @@ export default class GridData {
650
650
  */
651
651
  iterateArea(position, predicate, callback, visited = array(this.width, this.height, () => false)) {
652
652
  const tile = this.getTile(position.x, position.y);
653
- if (!tile.exists || !predicate(tile)) {
653
+ if (!tile.exists || !predicate(tile, position.x, position.y)) {
654
654
  return;
655
655
  }
656
656
  const stack = [position];
@@ -668,7 +668,7 @@ export default class GridData {
668
668
  const next = { x: x + offset.x, y: y + offset.y };
669
669
  if (this.isPositionValid(next.x, next.y)) {
670
670
  const nextTile = this.getTile(next.x, next.y);
671
- if (nextTile.exists && predicate(nextTile))
671
+ if (nextTile.exists && predicate(nextTile, next.x, next.y))
672
672
  stack.push(next);
673
673
  }
674
674
  }
@@ -687,7 +687,7 @@ export default class GridData {
687
687
  * @returns The value returned by the callback that stopped the iteration, or undefined if the iteration completed.
688
688
  */
689
689
  iterateDirection(position, direction, predicate, callback, visited = array(this.width, this.height, () => false)) {
690
- return this.iterateDirectionAll(position, direction, tile => tile.exists && predicate(tile), callback, visited);
690
+ return this.iterateDirectionAll(position, direction, (tile, logicalX, logicalY) => tile.exists && predicate(tile, logicalX, logicalY), callback, visited);
691
691
  }
692
692
  /**
693
693
  * Iterate over all tiles in a straight line from the given position in the given direction that satisfy the predicate.
@@ -710,7 +710,7 @@ export default class GridData {
710
710
  }
711
711
  visited[arrPos.y][arrPos.x] = true;
712
712
  const tile = this.getTile(current.x, current.y);
713
- if (!predicate(tile)) {
713
+ if (!predicate(tile, arrPos.x, arrPos.y)) {
714
714
  break;
715
715
  }
716
716
  const ret = callback(tile, arrPos.x, arrPos.y, current.x, current.y);
@@ -719,6 +719,51 @@ export default class GridData {
719
719
  current = move(current, direction);
720
720
  }
721
721
  }
722
+ /**
723
+ * Reduce the grid by zones defined in the GridZones.
724
+ *
725
+ * @param reducer The reducer function to apply to each zone.
726
+ * @param initializer The initializer function to create the initial value for each zone.
727
+ * @param visited A 2D array to keep track of visited tiles. This array is modified by the function.
728
+ * @returns An array of reduced values, one for each zone.
729
+ */
730
+ reduceByZone(reducer, initializer, visited = array(this.width, this.height, () => false)) {
731
+ const zones = [];
732
+ while (true) {
733
+ const seed = this.find((tile, x, y) => tile.exists && !visited[y][x]);
734
+ if (!seed)
735
+ break;
736
+ let zone = initializer();
737
+ const stack = [seed];
738
+ while (stack.length > 0) {
739
+ const { x, y } = stack.pop();
740
+ const { x: arrX, y: arrY } = this.toArrayCoordinates(x, y);
741
+ if (visited[arrY][arrX])
742
+ continue;
743
+ visited[arrY][arrX] = true;
744
+ zone = reducer(zone, this.getTile(arrX, arrY), arrX, arrY, x, y);
745
+ for (const offset of NEIGHBOR_OFFSETS) {
746
+ const next = this.toArrayCoordinates(x + offset.x, y + offset.y);
747
+ if (!this.zones.edges.some(e => {
748
+ const { x: x1, y: y1 } = this.toArrayCoordinates(e.x1, e.y1);
749
+ const { x: x2, y: y2 } = this.toArrayCoordinates(e.x2, e.y2);
750
+ return ((x1 === arrX &&
751
+ y1 === arrY &&
752
+ x2 === next.x &&
753
+ y2 === next.y) ||
754
+ (x2 === arrX && y2 === arrY && x1 === next.x && y1 === next.y));
755
+ })) {
756
+ const nextTile = this.getTile(next.x, next.y);
757
+ if (nextTile.exists) {
758
+ stack.push(next);
759
+ }
760
+ }
761
+ }
762
+ }
763
+ zones.push(zone);
764
+ }
765
+ return zones;
766
+ }
722
767
  /**
723
768
  * Check if every tile in the grid is filled with a color other than gray.
724
769
  *
@@ -1,5 +1,3 @@
1
- import { array } from '../dataHelper.js';
2
- import { NEIGHBOR_OFFSETS } from '../grid.js';
3
1
  import { Color } from '../primitives.js';
4
2
  import Rule from './rule.js';
5
3
  export default class CellCountPerZoneRule extends Rule {
@@ -21,49 +19,21 @@ export default class CellCountPerZoneRule extends Rule {
21
19
  }
22
20
  getZoneCounts(grid) {
23
21
  let complete = true;
24
- const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
25
- const zones = [];
26
- while (true) {
27
- const seed = grid.find((_tile, x, y) => !visited[y][x]);
28
- if (!seed)
29
- break;
30
- const zone = {
31
- positions: [],
32
- completed: 0,
33
- possible: 0,
34
- };
35
- const stack = [seed];
36
- while (stack.length > 0) {
37
- let { x, y } = stack.pop();
38
- ({ x, y } = grid.toArrayCoordinates(x, y));
39
- if (visited[y][x])
40
- continue;
41
- visited[y][x] = true;
42
- zone.positions.push({ x, y });
43
- if (grid.getTile(x, y).color === this.color) {
44
- zone.completed++;
45
- }
46
- else if (grid.getTile(x, y).color === Color.Gray) {
47
- zone.possible++;
48
- complete = false;
49
- }
50
- for (const offset of NEIGHBOR_OFFSETS) {
51
- const next = grid.toArrayCoordinates(x + offset.x, y + offset.y);
52
- if (!grid.zones.edges.some(e => {
53
- const { x: x1, y: y1 } = grid.toArrayCoordinates(e.x1, e.y1);
54
- const { x: x2, y: y2 } = grid.toArrayCoordinates(e.x2, e.y2);
55
- return ((x1 === x && y1 === y && x2 === next.x && y2 === next.y) ||
56
- (x2 === x && y2 === y && x1 === next.x && y1 === next.y));
57
- })) {
58
- const nextTile = grid.getTile(next.x, next.y);
59
- if (nextTile.exists) {
60
- stack.push(next);
61
- }
62
- }
63
- }
22
+ const zones = grid.reduceByZone((zone, tile, x, y) => {
23
+ zone.positions.push({ x, y });
24
+ if (tile.color === this.color) {
25
+ zone.completed++;
64
26
  }
65
- zones.push(zone);
66
- }
27
+ else if (tile.color === Color.Gray) {
28
+ zone.possible++;
29
+ complete = false;
30
+ }
31
+ return zone;
32
+ }, () => ({
33
+ positions: [],
34
+ completed: 0,
35
+ possible: 0,
36
+ }));
67
37
  return { zones, complete };
68
38
  }
69
39
  withColor(color) {
@@ -44,19 +44,18 @@ class ConnectAllRule extends Rule {
44
44
  }
45
45
  validateGrid(grid) {
46
46
  let complete = true;
47
- const visited = array(grid.width, grid.height, (i, j) => !(grid.getTile(i, j).exists && grid.getTile(i, j).color === this.color));
47
+ const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
48
48
  const islands = [];
49
49
  while (true) {
50
- const seed = grid.find((_tile, x, y) => !visited[y][x]);
50
+ const seed = grid.find((tile, x, y) => !visited[y][x] && tile.color === this.color);
51
51
  if (!seed)
52
52
  break;
53
53
  const positions = [];
54
54
  grid.iterateArea(seed, tile => tile.color === this.color || tile.color === Color.Gray, (tile, x, y) => {
55
- visited[y][x] = true;
56
55
  if (tile.color === Color.Gray)
57
56
  complete = false;
58
57
  positions.push({ x, y });
59
- });
58
+ }, visited);
60
59
  islands.push(positions);
61
60
  }
62
61
  if (islands.length > 1) {
@@ -0,0 +1,29 @@
1
+ import { AnyConfig } from '../config.js';
2
+ import GridData from '../grid.js';
3
+ import { Color, RuleState } from '../primitives.js';
4
+ import Rule, { SearchVariant } from './rule.js';
5
+ export default class ConnectZonesRule extends Rule {
6
+ readonly color: Color;
7
+ readonly title = "Connect Zones";
8
+ private static readonly CONFIGS;
9
+ private static readonly EXAMPLE_GRID_LIGHT;
10
+ private static readonly EXAMPLE_GRID_DARK;
11
+ private static readonly SEARCH_VARIANTS;
12
+ /**
13
+ * **Connect all &lt;color&gt; cells in each zone**
14
+ *
15
+ * @param color - The color of the cells to connect.
16
+ */
17
+ constructor(color: Color);
18
+ get id(): string;
19
+ get explanation(): string;
20
+ get configs(): readonly AnyConfig[] | null;
21
+ createExampleGrid(): GridData;
22
+ get searchVariants(): SearchVariant[];
23
+ validateGrid(grid: GridData): RuleState;
24
+ copyWith({ color }: {
25
+ color?: Color;
26
+ }): this;
27
+ withColor(color: Color): this;
28
+ }
29
+ export declare const instance: ConnectZonesRule;
@@ -0,0 +1,141 @@
1
+ import { ConfigType } from '../config.js';
2
+ import GridData from '../grid.js';
3
+ import { array, minBy } from '../dataHelper.js';
4
+ import { Color, State } from '../primitives.js';
5
+ import Rule from './rule.js';
6
+ import GridZones from '../gridZones.js';
7
+ class ConnectZonesRule extends Rule {
8
+ /**
9
+ * **Connect all &lt;color&gt; cells in each zone**
10
+ *
11
+ * @param color - The color of the cells to connect.
12
+ */
13
+ constructor(color) {
14
+ super();
15
+ Object.defineProperty(this, "color", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: color
20
+ });
21
+ Object.defineProperty(this, "title", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: 'Connect Zones'
26
+ });
27
+ this.color = color;
28
+ }
29
+ get id() {
30
+ return `connect_zones`;
31
+ }
32
+ get explanation() {
33
+ return `Connect all ${this.color} cells in each zone`;
34
+ }
35
+ get configs() {
36
+ return ConnectZonesRule.CONFIGS;
37
+ }
38
+ createExampleGrid() {
39
+ return this.color === Color.Light
40
+ ? ConnectZonesRule.EXAMPLE_GRID_LIGHT
41
+ : ConnectZonesRule.EXAMPLE_GRID_DARK;
42
+ }
43
+ get searchVariants() {
44
+ return ConnectZonesRule.SEARCH_VARIANTS;
45
+ }
46
+ validateGrid(grid) {
47
+ let complete = true;
48
+ let zoneId = 1;
49
+ const zoneMap = array(grid.width, grid.height, () => 0);
50
+ const visited = array(grid.width, grid.height, (i, j) => !grid.getTile(i, j).exists);
51
+ const zones = grid.reduceByZone((zone, tile, x, y) => {
52
+ zoneMap[y][x] = zone;
53
+ if (tile.exists && tile.color === Color.Gray) {
54
+ complete = false;
55
+ }
56
+ return zone;
57
+ }, () => zoneId++);
58
+ for (const zone of zones) {
59
+ const islands = [];
60
+ while (true) {
61
+ const seed = grid.find((tile, x, y) => !visited[y][x] &&
62
+ zoneMap[y][x] === zone &&
63
+ tile.color === this.color);
64
+ if (!seed)
65
+ break;
66
+ const positions = [];
67
+ grid.iterateArea(seed, (tile, x, y) => {
68
+ if (tile.color !== this.color && tile.color !== Color.Gray) {
69
+ return false;
70
+ }
71
+ const pos = grid.toArrayCoordinates(x, y);
72
+ return zoneMap[pos.y][pos.x] === zone;
73
+ }, (tile, x, y) => {
74
+ if (tile.color === Color.Gray)
75
+ complete = false;
76
+ positions.push({ x, y });
77
+ }, visited);
78
+ islands.push(positions);
79
+ }
80
+ if (islands.length > 1) {
81
+ return {
82
+ state: State.Error,
83
+ positions: minBy(islands, island => island.length),
84
+ };
85
+ }
86
+ }
87
+ return { state: complete ? State.Satisfied : State.Incomplete };
88
+ }
89
+ copyWith({ color }) {
90
+ return new ConnectZonesRule(color ?? this.color);
91
+ }
92
+ withColor(color) {
93
+ return this.copyWith({ color });
94
+ }
95
+ }
96
+ Object.defineProperty(ConnectZonesRule, "CONFIGS", {
97
+ enumerable: true,
98
+ configurable: true,
99
+ writable: true,
100
+ value: Object.freeze([
101
+ {
102
+ type: ConfigType.Color,
103
+ default: Color.Light,
104
+ allowGray: false,
105
+ field: 'color',
106
+ description: 'Color',
107
+ configurable: true,
108
+ },
109
+ ])
110
+ });
111
+ Object.defineProperty(ConnectZonesRule, "EXAMPLE_GRID_LIGHT", {
112
+ enumerable: true,
113
+ configurable: true,
114
+ writable: true,
115
+ value: Object.freeze(GridData.create(['wbbwb', 'wbbwb', 'wbbww', 'wwbbb'])
116
+ .withZones(new GridZones([
117
+ { x1: 2, y1: 1, x2: 2, y2: 2 },
118
+ { x1: 1, y1: 0, x2: 2, y2: 0 },
119
+ { x1: 1, y1: 1, x2: 2, y2: 1 },
120
+ { x1: 2, y1: 2, x2: 3, y2: 2 },
121
+ { x1: 2, y1: 3, x2: 3, y2: 3 },
122
+ ]))
123
+ .addRule(new ConnectZonesRule(Color.Light)))
124
+ });
125
+ Object.defineProperty(ConnectZonesRule, "EXAMPLE_GRID_DARK", {
126
+ enumerable: true,
127
+ configurable: true,
128
+ writable: true,
129
+ value: Object.freeze(ConnectZonesRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))))
130
+ });
131
+ Object.defineProperty(ConnectZonesRule, "SEARCH_VARIANTS", {
132
+ enumerable: true,
133
+ configurable: true,
134
+ writable: true,
135
+ value: [
136
+ new ConnectZonesRule(Color.Light).searchVariant(),
137
+ new ConnectZonesRule(Color.Dark).searchVariant(),
138
+ ]
139
+ });
140
+ export default ConnectZonesRule;
141
+ export const instance = new ConnectZonesRule(Color.Dark);
@@ -0,0 +1,33 @@
1
+ import { AnyConfig } from '../config.js';
2
+ import GridData from '../grid.js';
3
+ import { Color, RuleState } from '../primitives.js';
4
+ import CellCountPerZoneRule from './cellCountPerZoneRule.js';
5
+ import { SearchVariant } from './rule.js';
6
+ export default class ExactCountPerZoneRule extends CellCountPerZoneRule {
7
+ readonly color: Color;
8
+ readonly count: number;
9
+ readonly title = "Exact Count Per Zone";
10
+ private static readonly CONFIGS;
11
+ private static readonly EXAMPLE_GRID_LIGHT;
12
+ private static readonly EXAMPLE_GRID_DARK;
13
+ private static readonly EXAMPLE_GRID_GRAY;
14
+ private static readonly SEARCH_VARIANTS;
15
+ /**
16
+ * **Each zone has &lt;count&gt; &lt;color&gt; cells.**
17
+ *
18
+ * @param color - The color of the cells to count.
19
+ * @param count - The exact count of the cells in each zone.
20
+ */
21
+ constructor(color: Color, count: number);
22
+ get id(): string;
23
+ get explanation(): string;
24
+ get configs(): readonly AnyConfig[] | null;
25
+ createExampleGrid(): GridData;
26
+ get searchVariants(): SearchVariant[];
27
+ validateGrid(grid: GridData): RuleState;
28
+ copyWith({ color, count }: {
29
+ color?: Color;
30
+ count?: number;
31
+ }): this;
32
+ }
33
+ export declare const instance: ExactCountPerZoneRule;
@@ -0,0 +1,138 @@
1
+ import { ConfigType } from '../config.js';
2
+ import GridData from '../grid.js';
3
+ import GridZones from '../gridZones.js';
4
+ import { Color, State } from '../primitives.js';
5
+ import CellCountPerZoneRule from './cellCountPerZoneRule.js';
6
+ class ExactCountPerZoneRule extends CellCountPerZoneRule {
7
+ /**
8
+ * **Each zone has &lt;count&gt; &lt;color&gt; cells.**
9
+ *
10
+ * @param color - The color of the cells to count.
11
+ * @param count - The exact count of the cells in each zone.
12
+ */
13
+ constructor(color, count) {
14
+ super(color);
15
+ Object.defineProperty(this, "color", {
16
+ enumerable: true,
17
+ configurable: true,
18
+ writable: true,
19
+ value: color
20
+ });
21
+ Object.defineProperty(this, "count", {
22
+ enumerable: true,
23
+ configurable: true,
24
+ writable: true,
25
+ value: count
26
+ });
27
+ Object.defineProperty(this, "title", {
28
+ enumerable: true,
29
+ configurable: true,
30
+ writable: true,
31
+ value: 'Exact Count Per Zone'
32
+ });
33
+ this.count = count;
34
+ }
35
+ get id() {
36
+ return `zone_exact_count`;
37
+ }
38
+ get explanation() {
39
+ return `Each zone has exactly ${this.count} ${this.color} cell${this.count === 1 ? '' : 's'}`;
40
+ }
41
+ get configs() {
42
+ return ExactCountPerZoneRule.CONFIGS;
43
+ }
44
+ createExampleGrid() {
45
+ if (this.color === Color.Light) {
46
+ return ExactCountPerZoneRule.EXAMPLE_GRID_LIGHT;
47
+ }
48
+ else if (this.color === Color.Dark) {
49
+ return ExactCountPerZoneRule.EXAMPLE_GRID_DARK;
50
+ }
51
+ else {
52
+ return ExactCountPerZoneRule.EXAMPLE_GRID_GRAY;
53
+ }
54
+ }
55
+ get searchVariants() {
56
+ return ExactCountPerZoneRule.SEARCH_VARIANTS;
57
+ }
58
+ validateGrid(grid) {
59
+ const { zones, complete } = this.getZoneCounts(grid);
60
+ const errorZone = zones.find(z => z.completed > this.count || z.completed + z.possible < this.count);
61
+ if (errorZone) {
62
+ return {
63
+ state: State.Error,
64
+ positions: errorZone.positions,
65
+ };
66
+ }
67
+ else {
68
+ return { state: complete ? State.Satisfied : State.Incomplete };
69
+ }
70
+ }
71
+ copyWith({ color, count }) {
72
+ return new ExactCountPerZoneRule(color ?? this.color, count ?? this.count);
73
+ }
74
+ }
75
+ Object.defineProperty(ExactCountPerZoneRule, "CONFIGS", {
76
+ enumerable: true,
77
+ configurable: true,
78
+ writable: true,
79
+ value: Object.freeze([
80
+ {
81
+ type: ConfigType.Color,
82
+ default: Color.Light,
83
+ allowGray: true,
84
+ field: 'color',
85
+ description: 'Color',
86
+ configurable: true,
87
+ },
88
+ {
89
+ type: ConfigType.Number,
90
+ default: 1,
91
+ min: 0,
92
+ field: 'count',
93
+ description: 'Count',
94
+ configurable: true,
95
+ },
96
+ ])
97
+ });
98
+ Object.defineProperty(ExactCountPerZoneRule, "EXAMPLE_GRID_LIGHT", {
99
+ enumerable: true,
100
+ configurable: true,
101
+ writable: true,
102
+ value: Object.freeze(GridData.create(['wbbbb', 'bbbwb', 'bbbwb', 'bwbbb'])
103
+ .withZones(new GridZones([
104
+ { x1: 0, y1: 1, x2: 0, y2: 2 },
105
+ { x1: 1, y1: 1, x2: 1, y2: 2 },
106
+ { x1: 2, y1: 1, x2: 2, y2: 2 },
107
+ { x1: 3, y1: 1, x2: 3, y2: 2 },
108
+ { x1: 4, y1: 1, x2: 4, y2: 2 },
109
+ { x1: 1, y1: 0, x2: 2, y2: 0 },
110
+ { x1: 1, y1: 1, x2: 2, y2: 1 },
111
+ { x1: 2, y1: 2, x2: 3, y2: 2 },
112
+ { x1: 2, y1: 3, x2: 3, y2: 3 },
113
+ ]))
114
+ .addRule(new ExactCountPerZoneRule(Color.Light, 1)))
115
+ });
116
+ Object.defineProperty(ExactCountPerZoneRule, "EXAMPLE_GRID_DARK", {
117
+ enumerable: true,
118
+ configurable: true,
119
+ writable: true,
120
+ value: Object.freeze(ExactCountPerZoneRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Dark ? Color.Light : Color.Dark)))))
121
+ });
122
+ Object.defineProperty(ExactCountPerZoneRule, "EXAMPLE_GRID_GRAY", {
123
+ enumerable: true,
124
+ configurable: true,
125
+ writable: true,
126
+ value: Object.freeze(ExactCountPerZoneRule.EXAMPLE_GRID_LIGHT.withTiles(tiles => tiles.map(row => row.map(tile => tile.withColor(tile.color === Color.Light ? Color.Gray : tile.color)))))
127
+ });
128
+ Object.defineProperty(ExactCountPerZoneRule, "SEARCH_VARIANTS", {
129
+ enumerable: true,
130
+ configurable: true,
131
+ writable: true,
132
+ value: [
133
+ new ExactCountPerZoneRule(Color.Light, 1).searchVariant(),
134
+ new ExactCountPerZoneRule(Color.Dark, 1).searchVariant(),
135
+ ]
136
+ });
137
+ export default ExactCountPerZoneRule;
138
+ export const instance = new ExactCountPerZoneRule(Color.Light, 1);
@@ -2,9 +2,11 @@ export { instance as BanPatternRule } from './banPatternRule.js';
2
2
  export { instance as CellCountRule } from './cellCountRule.js';
3
3
  export { instance as CompletePatternRule } from './completePatternRule.js';
4
4
  export { instance as ConnectAllRule } from './connectAllRule.js';
5
+ export { instance as ConnectZonesRule } from './connectZonesRule.js';
5
6
  export { instance as ContainsShapeRule } from './containsShapeRule.js';
6
7
  export { instance as CustomRule } from './customRule.js';
7
8
  export { instance as DifferentCountPerZoneRule } from './differentCountPerZoneRule.js';
9
+ export { instance as ExactCountPerZoneRule } from './exactCountPerZoneRule.js';
8
10
  export { instance as ForesightRule } from './foresightRule.js';
9
11
  export { instance as LyingSymbolRule } from './lyingSymbolRule.js';
10
12
  export { instance as MusicGridRule } from './musicGridRule.js';
@@ -6,9 +6,11 @@ export { instance as BanPatternRule } from './banPatternRule.js';
6
6
  export { instance as CellCountRule } from './cellCountRule.js';
7
7
  export { instance as CompletePatternRule } from './completePatternRule.js';
8
8
  export { instance as ConnectAllRule } from './connectAllRule.js';
9
+ export { instance as ConnectZonesRule } from './connectZonesRule.js';
9
10
  export { instance as ContainsShapeRule } from './containsShapeRule.js';
10
11
  export { instance as CustomRule } from './customRule.js';
11
12
  export { instance as DifferentCountPerZoneRule } from './differentCountPerZoneRule.js';
13
+ export { instance as ExactCountPerZoneRule } from './exactCountPerZoneRule.js';
12
14
  export { instance as ForesightRule } from './foresightRule.js';
13
15
  export { instance as LyingSymbolRule } from './lyingSymbolRule.js';
14
16
  export { instance as MusicGridRule } from './musicGridRule.js';
package/dist/index.d.ts CHANGED
@@ -20,9 +20,11 @@ import CellCountPerZoneRule from './data/rules/cellCountPerZoneRule.js';
20
20
  import CellCountRule from './data/rules/cellCountRule.js';
21
21
  import CompletePatternRule from './data/rules/completePatternRule.js';
22
22
  import ConnectAllRule from './data/rules/connectAllRule.js';
23
+ import ConnectZonesRule from './data/rules/connectZonesRule.js';
23
24
  import ContainsShapeRule from './data/rules/containsShapeRule.js';
24
25
  import CustomRule from './data/rules/customRule.js';
25
26
  import DifferentCountPerZoneRule from './data/rules/differentCountPerZoneRule.js';
27
+ import ExactCountPerZoneRule from './data/rules/exactCountPerZoneRule.js';
26
28
  import ForesightRule from './data/rules/foresightRule.js';
27
29
  import { allRules } from './data/rules/index.js';
28
30
  import LyingSymbolRule from './data/rules/lyingSymbolRule.js';
@@ -114,4 +116,4 @@ import TileData from './data/tile.js';
114
116
  import TileConnections from './data/tileConnections.js';
115
117
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
116
118
  import { GridValidator } from './data/validateAsync.js';
117
- export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGetTile, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, DRUM_SAMPLES, Direction, INSTRUMENTS, Instrument, MajorRule, Mode, ORIENTATIONS, Orientation, PuzzleType, State, WRAPPINGS, Wrapping, directionToggle, isDrumSample, orientationToggle, MetadataSchema, PuzzleSchema, getPuzzleTypes, puzzleEquals, validatePuzzleChecklist, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ContainsShapeRule, CustomRule, DifferentCountPerZoneRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameCountPerZoneRule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, ChecksumCompressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerChecksum, SerializerV0, OFFSETS, orientationChars, getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape, allSolvers, AutoSolver, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, FocusBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, CspuzSolver, gridToJson, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, FocusSymbol, GalaxySymbol, HiddenSymbol, HouseSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, GridValidator, };
119
+ export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGetTile, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, DRUM_SAMPLES, Direction, INSTRUMENTS, Instrument, MajorRule, Mode, ORIENTATIONS, Orientation, PuzzleType, State, WRAPPINGS, Wrapping, directionToggle, isDrumSample, orientationToggle, MetadataSchema, PuzzleSchema, getPuzzleTypes, puzzleEquals, validatePuzzleChecklist, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ConnectZonesRule, ContainsShapeRule, CustomRule, DifferentCountPerZoneRule, ExactCountPerZoneRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameCountPerZoneRule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, ChecksumCompressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerChecksum, SerializerV0, OFFSETS, orientationChars, getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape, allSolvers, AutoSolver, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, FocusBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, CspuzSolver, gridToJson, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, FocusSymbol, GalaxySymbol, HiddenSymbol, HouseSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, GridValidator, };
package/dist/index.js CHANGED
@@ -23,9 +23,11 @@ import CellCountPerZoneRule from './data/rules/cellCountPerZoneRule.js';
23
23
  import CellCountRule from './data/rules/cellCountRule.js';
24
24
  import CompletePatternRule from './data/rules/completePatternRule.js';
25
25
  import ConnectAllRule from './data/rules/connectAllRule.js';
26
+ import ConnectZonesRule from './data/rules/connectZonesRule.js';
26
27
  import ContainsShapeRule from './data/rules/containsShapeRule.js';
27
28
  import CustomRule from './data/rules/customRule.js';
28
29
  import DifferentCountPerZoneRule from './data/rules/differentCountPerZoneRule.js';
30
+ import ExactCountPerZoneRule from './data/rules/exactCountPerZoneRule.js';
29
31
  import ForesightRule from './data/rules/foresightRule.js';
30
32
  import { allRules } from './data/rules/index.js';
31
33
  import LyingSymbolRule from './data/rules/lyingSymbolRule.js';
@@ -117,4 +119,4 @@ import TileData from './data/tile.js';
117
119
  import TileConnections from './data/tileConnections.js';
118
120
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
119
121
  import { GridValidator } from './data/validateAsync.js';
120
- export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGetTile, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, DRUM_SAMPLES, Direction, INSTRUMENTS, Instrument, MajorRule, Mode, ORIENTATIONS, Orientation, PuzzleType, State, WRAPPINGS, Wrapping, directionToggle, isDrumSample, orientationToggle, MetadataSchema, PuzzleSchema, getPuzzleTypes, puzzleEquals, validatePuzzleChecklist, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ContainsShapeRule, CustomRule, DifferentCountPerZoneRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameCountPerZoneRule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, ChecksumCompressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerChecksum, SerializerV0, OFFSETS, orientationChars, getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape, allSolvers, AutoSolver, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, FocusBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, CspuzSolver, gridToJson, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, FocusSymbol, GalaxySymbol, HiddenSymbol, HouseSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, GridValidator, };
122
+ export { ConfigType, configEquals, Configurable, CachedAccess, allEqual, array, directionToRotation, escape, isSameEdge, maxBy, minBy, move, orientationToRotation, resize, unescape, isEventHandler, handlesFinalValidation, handlesGetTile, handlesGridChange, handlesGridResize, handlesSetGrid, invokeSetGrid, handlesSymbolDisplay, handlesSymbolValidation, GridData, NEIGHBOR_OFFSETS, GridConnections, GridZones, Instruction, COMPARISONS, Color, Comparison, DIRECTIONS, DRUM_SAMPLES, Direction, INSTRUMENTS, Instrument, MajorRule, Mode, ORIENTATIONS, Orientation, PuzzleType, State, WRAPPINGS, Wrapping, directionToggle, isDrumSample, orientationToggle, MetadataSchema, PuzzleSchema, getPuzzleTypes, puzzleEquals, validatePuzzleChecklist, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ConnectZonesRule, ContainsShapeRule, CustomRule, DifferentCountPerZoneRule, ExactCountPerZoneRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameCountPerZoneRule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, ChecksumCompressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerChecksum, SerializerV0, OFFSETS, orientationChars, getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape, allSolvers, AutoSolver, BacktrackSolver, BTModule, BTGridData, BTTile, IntArray2D, colorToBTTile, createOneTileResult, getOppositeColor, BanPatternBTModule, CellCountBTModule, ConnectAllBTModule, RegionAreaBTModule, RegionShapeBTModule, SameShapeBTModule, SymbolsPerRegionBTModule, UniqueShapeBTModule, AreaNumberBTModule, DartBTModule, DirectionLinkerBTModule, FocusBTModule, GalaxyBTModule, LetterBTModule, LotusBTModule, MinesweeperBTModule, MyopiaBTModule, ViewpointBTModule, CspuzSolver, gridToJson, EventIteratingSolver, Solver, UniversalSolver, AreaNumberModule, CellCountModule, ConnectAllModule, DartModule, allZ3Modules, LetterModule, MyopiaModule, RegionAreaModule, ViewpointModule, Z3Module, convertDirection, Z3Solver, Z3SolverContext, AreaNumberSymbol, CustomIconSymbol, CustomSymbol, CustomTextSymbol, DartSymbol, DirectionLinkerSymbol, FocusSymbol, GalaxySymbol, HiddenSymbol, HouseSymbol, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, GridValidator, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.21.0",
3
+ "version": "0.22.1",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",