@logic-pad/core 0.10.2 → 0.11.3

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.
@@ -160,6 +160,11 @@ declare global {
160
160
  * @returns The deduplicated array of edges.
161
161
  */
162
162
  static deduplicateEdges(edges: readonly Edge[]): readonly Edge[];
163
+ static validateEdges(
164
+ connections: GridZones,
165
+ width: number,
166
+ height: number
167
+ ): GridZones;
163
168
  insertColumn(index: number): GridZones;
164
169
  insertRow(index: number): GridZones;
165
170
  removeColumn(index: number): GridZones;
@@ -206,6 +211,11 @@ declare global {
206
211
  * @returns The created connections. You can apply this to a GridData object using GridData.withConnections.
207
212
  */
208
213
  static create(array: string[]): GridConnections;
214
+ static validateEdges(
215
+ connections: GridConnections,
216
+ width: number,
217
+ height: number
218
+ ): GridConnections;
209
219
  insertColumn(index: number): GridConnections;
210
220
  insertRow(index: number): GridConnections;
211
221
  removeColumn(index: number): GridConnections;
@@ -1495,6 +1505,10 @@ declare global {
1495
1505
  ): Shape;
1496
1506
  export declare function getShapeVariants(shape: Shape): Shape[];
1497
1507
  export declare function normalizeShape(shape: Shape): Shape;
1508
+ export declare function sanitizePatternGrid(
1509
+ pattern: GridData,
1510
+ tileMapper?: (tile: TileData) => TileData
1511
+ ): GridData;
1498
1512
  export declare class BanPatternRule extends Rule {
1499
1513
  private static readonly EXAMPLE_GRID;
1500
1514
  private static readonly CONFIGS;
@@ -1583,6 +1597,46 @@ declare global {
1583
1597
  copyWith({ color }: { color?: Color }): this;
1584
1598
  withColor(color: Color): this;
1585
1599
  }
1600
+ export type ShapeRegions = {
1601
+ regions: {
1602
+ positions: Position$1[];
1603
+ shape: Shape;
1604
+ count: number;
1605
+ }[];
1606
+ complete: boolean;
1607
+ };
1608
+ export declare abstract class RegionShapeRule extends Rule {
1609
+ readonly color: Color;
1610
+ /**
1611
+ * @param color - The color of the regions to compare.
1612
+ */
1613
+ constructor(color: Color);
1614
+ protected getShapeRegions(grid: GridData): ShapeRegions;
1615
+ withColor(color: Color): this;
1616
+ }
1617
+ export declare class ContainsShapeRule extends RegionShapeRule {
1618
+ private static readonly EXAMPLE_GRID_LIGHT;
1619
+ private static readonly EXAMPLE_GRID_DARK;
1620
+ private static readonly CONFIGS;
1621
+ private static readonly SEARCH_VARIANTS;
1622
+ readonly pattern: GridData;
1623
+ readonly cache: Shape[];
1624
+ /**
1625
+ * **All <color> areas must contain this pattern**
1626
+ *
1627
+ * @param color - The color of the regions to compare.
1628
+ * @param pattern - GridData representing the required pattern. Only non-gray tiles are considered.
1629
+ */
1630
+ constructor(color: Color, pattern: GridData);
1631
+ get id(): string;
1632
+ get explanation(): string;
1633
+ get configs(): readonly AnyConfig[] | null;
1634
+ createExampleGrid(): GridData;
1635
+ get searchVariants(): SearchVariant[];
1636
+ validateGrid(grid: GridData): RuleState;
1637
+ copyWith({ color, pattern }: { color?: Color; pattern?: GridData }): this;
1638
+ withPattern(pattern: GridData): this;
1639
+ }
1586
1640
  export declare class CustomRule extends Rule {
1587
1641
  readonly description: string;
1588
1642
  readonly grid: GridData;
@@ -1825,23 +1879,6 @@ declare global {
1825
1879
  withColor(color: Color): this;
1826
1880
  withSize(size: number): this;
1827
1881
  }
1828
- export type ShapeRegions = {
1829
- regions: {
1830
- positions: Position$1[];
1831
- shape: Shape;
1832
- count: number;
1833
- }[];
1834
- complete: boolean;
1835
- };
1836
- export declare abstract class RegionShapeRule extends Rule {
1837
- readonly color: Color;
1838
- /**
1839
- * @param color - The color of the regions to compare.
1840
- */
1841
- constructor(color: Color);
1842
- protected getShapeRegions(grid: GridData): ShapeRegions;
1843
- withColor(color: Color): this;
1844
- }
1845
1882
  export declare class SameShapeRule extends RegionShapeRule {
1846
1883
  private static readonly CONFIGS;
1847
1884
  private static readonly EXAMPLE_GRID_LIGHT;
@@ -2115,10 +2152,14 @@ declare global {
2115
2152
  readonly description =
2116
2153
  'Automatically select the fastest solver based on supported instructions and environment.';
2117
2154
  readonly supportsCancellation = true;
2118
- private gridSupportCache;
2155
+ private static readonly nonAdditiveInstructions;
2119
2156
  isGridSupported(grid: GridData): boolean;
2120
2157
  isInstructionSupported(instructionId: string): boolean;
2121
2158
  protected isEnvironmentSupported(): Promise<boolean>;
2159
+ private fillSolution;
2160
+ private fixGrid;
2161
+ private solveWithProgress;
2162
+ private solveOne;
2122
2163
  solve(
2123
2164
  grid: GridData,
2124
2165
  abortSignal?: AbortSignal | undefined
package/dist/data/grid.js CHANGED
@@ -109,7 +109,9 @@ export default class GridData {
109
109
  : new Map();
110
110
  // do not deduplicate all rules because it makes for bad editor experience
111
111
  const newRules = rules ? GridData.deduplicateSingletonRules(rules) : [];
112
- const newGrid = new GridData(arrayOrWidth, height, tiles, connections, zones, newSymbols, newRules);
112
+ 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);
113
115
  newSymbols.forEach(list => {
114
116
  list.forEach((sym, i) => {
115
117
  if (handlesGridChange(sym)) {
@@ -17,6 +17,7 @@ export default class GridConnections extends GridZones {
17
17
  * @returns The created connections. You can apply this to a GridData object using GridData.withConnections.
18
18
  */
19
19
  static create(array: string[]): GridConnections;
20
+ static validateEdges(connections: GridConnections, width: number, height: number): GridConnections;
20
21
  insertColumn(index: number): GridConnections;
21
22
  insertRow(index: number): GridConnections;
22
23
  removeColumn(index: number): GridConnections;
@@ -111,6 +111,25 @@ export default class GridConnections extends GridZones {
111
111
  }
112
112
  return new GridConnections(edges);
113
113
  }
114
+ static validateEdges(connections, width, height) {
115
+ const newEdges = [];
116
+ for (const edge of connections.edges) {
117
+ if (edge.x1 < 0 || edge.x1 >= width || edge.y1 < 0 || edge.y1 >= height) {
118
+ continue;
119
+ }
120
+ if (edge.x2 < 0 || edge.x2 >= width || edge.y2 < 0 || edge.y2 >= height) {
121
+ continue;
122
+ }
123
+ if (Math.abs(edge.x1 - edge.x2) + Math.abs(edge.y1 - edge.y2) !== 1) {
124
+ continue;
125
+ }
126
+ newEdges.push(edge);
127
+ }
128
+ if (newEdges.length === connections.edges.length) {
129
+ return connections;
130
+ }
131
+ return new GridConnections(newEdges);
132
+ }
114
133
  insertColumn(index) {
115
134
  return new GridConnections(this.edges.flatMap(edge => {
116
135
  if ((edge.x1 < index && edge.x2 < index) ||
@@ -18,6 +18,7 @@ export default class GridZones {
18
18
  * @returns The deduplicated array of edges.
19
19
  */
20
20
  static deduplicateEdges(edges: readonly Edge[]): readonly Edge[];
21
+ static validateEdges(connections: GridZones, width: number, height: number): GridZones;
21
22
  insertColumn(index: number): GridZones;
22
23
  insertRow(index: number): GridZones;
23
24
  removeColumn(index: number): GridZones;
@@ -48,6 +48,37 @@ export default class GridZones {
48
48
  static deduplicateEdges(edges) {
49
49
  return edges.filter((edge, index) => edges.findIndex(e => isSameEdge(e, edge)) === index);
50
50
  }
51
+ static validateEdges(connections, width, height) {
52
+ const newEdges = [];
53
+ for (const edge of connections.edges) {
54
+ if (edge.x1 < -1 ||
55
+ edge.x1 >= width + 1 ||
56
+ edge.y1 < -1 ||
57
+ edge.y1 >= height + 1) {
58
+ continue;
59
+ }
60
+ if (edge.x2 < -1 ||
61
+ edge.x2 >= width + 1 ||
62
+ edge.y2 < -1 ||
63
+ edge.y2 >= height + 1) {
64
+ continue;
65
+ }
66
+ if ((edge.x1 < 0 && edge.x2 < 0) ||
67
+ (edge.y1 < 0 && edge.y2 < 0) ||
68
+ (edge.x1 >= width && edge.x2 >= width) ||
69
+ (edge.y1 >= height && edge.y2 >= height)) {
70
+ continue;
71
+ }
72
+ if (Math.abs(edge.x1 - edge.x2) + Math.abs(edge.y1 - edge.y2) !== 1) {
73
+ continue;
74
+ }
75
+ newEdges.push(edge);
76
+ }
77
+ if (newEdges.length === connections.edges.length) {
78
+ return connections;
79
+ }
80
+ return new GridZones(newEdges);
81
+ }
51
82
  insertColumn(index) {
52
83
  return new GridZones(this.edges.map(edge => {
53
84
  if (edge.x1 < index || edge.x2 < index) {
@@ -2,7 +2,7 @@ import { ConfigType } from '../config.js';
2
2
  import GridData from '../grid.js';
3
3
  import { array } from '../dataHelper.js';
4
4
  import { Color, State } from '../primitives.js';
5
- import { getShapeVariants, tilesToShape } from '../shapes.js';
5
+ import { getShapeVariants, sanitizePatternGrid, tilesToShape, } from '../shapes.js';
6
6
  import Rule from './rule.js';
7
7
  class BanPatternRule extends Rule {
8
8
  /**
@@ -24,14 +24,7 @@ class BanPatternRule extends Rule {
24
24
  writable: true,
25
25
  value: void 0
26
26
  });
27
- this.pattern = pattern
28
- // unlock all tiles
29
- .withTiles(tiles => tiles.map(row => row.map(t => t.exists
30
- ? t.withFixed(false)
31
- : t.copyWith({ exists: true, color: Color.Gray, fixed: false }))))
32
- // strip all symbols and rules
33
- .withRules([])
34
- .withSymbols(new Map());
27
+ this.pattern = sanitizePatternGrid(pattern);
35
28
  this.cache = getShapeVariants(tilesToShape(this.pattern.tiles));
36
29
  }
37
30
  get id() {
@@ -71,13 +64,26 @@ class BanPatternRule extends Rule {
71
64
  }
72
65
  validateGrid(grid) {
73
66
  for (const pattern of this.cache) {
74
- for (let y = 0; y <= grid.height - 1; y++) {
75
- for (let x = 0; x <= grid.width - 1; x++) {
67
+ let startX, startY, endX, endY;
68
+ if (grid.wrapAround.value) {
69
+ startX = -pattern.width;
70
+ startY = -pattern.height;
71
+ endX = grid.width - 1;
72
+ endY = grid.height - 1;
73
+ }
74
+ else {
75
+ startX = 0;
76
+ startY = 0;
77
+ endX = grid.width - pattern.width;
78
+ endY = grid.height - pattern.height;
79
+ }
80
+ for (let y = startY; y <= endY; y++) {
81
+ for (let x = startX; x <= endX; x++) {
76
82
  let match = true;
77
83
  const visited = [];
78
84
  for (const tile of pattern.elements) {
79
85
  const pos = grid.toArrayCoordinates(x + tile.x, y + tile.y);
80
- if (grid.wrapAround.value && // optimization: not need to check visited if wrapAround is disabled
86
+ if (grid.wrapAround.value && // optimization: no need to check visited if wrapAround is disabled
81
87
  visited.some(p => p.x === pos.x && p.y === pos.y)) {
82
88
  match = false;
83
89
  break;
@@ -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 { Shape } from '../shapes.js';
5
+ import RegionShapeRule from './regionShapeRule.js';
6
+ import { SearchVariant } from './rule.js';
7
+ export default class ContainsShapeRule extends RegionShapeRule {
8
+ private static readonly EXAMPLE_GRID_LIGHT;
9
+ private static readonly EXAMPLE_GRID_DARK;
10
+ private static readonly CONFIGS;
11
+ private static readonly SEARCH_VARIANTS;
12
+ readonly pattern: GridData;
13
+ readonly cache: Shape[];
14
+ /**
15
+ * **All &lt;color&gt; areas must contain this pattern**
16
+ *
17
+ * @param color - The color of the regions to compare.
18
+ * @param pattern - GridData representing the required pattern. Only non-gray tiles are considered.
19
+ */
20
+ constructor(color: Color, pattern: GridData);
21
+ get id(): string;
22
+ get explanation(): string;
23
+ get configs(): readonly AnyConfig[] | null;
24
+ createExampleGrid(): GridData;
25
+ get searchVariants(): SearchVariant[];
26
+ validateGrid(grid: GridData): RuleState;
27
+ copyWith({ color, pattern, }: {
28
+ color?: Color;
29
+ pattern?: GridData;
30
+ }): this;
31
+ withPattern(pattern: GridData): this;
32
+ }
33
+ export declare const instance: ContainsShapeRule;
@@ -0,0 +1,151 @@
1
+ import { ConfigType } from '../config.js';
2
+ import { array } from '../dataHelper.js';
3
+ import GridData from '../grid.js';
4
+ import { Color, State } from '../primitives.js';
5
+ import { getShapeVariants, sanitizePatternGrid, tilesToShape, } from '../shapes.js';
6
+ import RegionShapeRule from './regionShapeRule.js';
7
+ class ContainsShapeRule extends RegionShapeRule {
8
+ /**
9
+ * **All &lt;color&gt; areas must contain this pattern**
10
+ *
11
+ * @param color - The color of the regions to compare.
12
+ * @param pattern - GridData representing the required pattern. Only non-gray tiles are considered.
13
+ */
14
+ constructor(color, pattern) {
15
+ super(color);
16
+ Object.defineProperty(this, "pattern", {
17
+ enumerable: true,
18
+ configurable: true,
19
+ writable: true,
20
+ value: void 0
21
+ });
22
+ Object.defineProperty(this, "cache", {
23
+ enumerable: true,
24
+ configurable: true,
25
+ writable: true,
26
+ value: void 0
27
+ });
28
+ this.pattern = sanitizePatternGrid(pattern, t => t.color === color ? t : t.withColor(Color.Gray));
29
+ this.cache = getShapeVariants(tilesToShape(this.pattern.tiles));
30
+ }
31
+ get id() {
32
+ return `contains_shape`;
33
+ }
34
+ get explanation() {
35
+ return `All ${this.color} areas must contain this pattern`;
36
+ }
37
+ get configs() {
38
+ return ContainsShapeRule.CONFIGS;
39
+ }
40
+ createExampleGrid() {
41
+ let minX = Number.POSITIVE_INFINITY;
42
+ let minY = Number.POSITIVE_INFINITY;
43
+ let maxX = Number.NEGATIVE_INFINITY;
44
+ let maxY = Number.NEGATIVE_INFINITY;
45
+ this.pattern.forEach((tile, x, y) => {
46
+ if (tile.color !== Color.Gray && tile.exists) {
47
+ minX = Math.min(minX, x);
48
+ minY = Math.min(minY, y);
49
+ maxX = Math.max(maxX, x);
50
+ maxY = Math.max(maxY, y);
51
+ }
52
+ });
53
+ const width = maxX - minX + 1;
54
+ const height = maxY - minY + 1;
55
+ const tiles = array(width, height, (x, y) => {
56
+ const tile = this.pattern.getTile(x + minX, y + minY);
57
+ if (!tile.exists || tile.color !== Color.Gray)
58
+ return tile;
59
+ return tile.withExists(false);
60
+ });
61
+ return GridData.create(width, height, tiles);
62
+ }
63
+ get searchVariants() {
64
+ return ContainsShapeRule.SEARCH_VARIANTS;
65
+ }
66
+ validateGrid(grid) {
67
+ const { regions, complete } = this.getShapeRegions(grid);
68
+ const errorRegion = regions.find(r => {
69
+ for (const pattern of this.cache) {
70
+ if (r.shape.elements.length < pattern.elements.length)
71
+ continue;
72
+ for (let y = 0; y <= r.shape.height - pattern.height; y++) {
73
+ for (let x = 0; x <= r.shape.width - pattern.width; x++) {
74
+ let match = true;
75
+ for (const element of pattern.elements) {
76
+ const tile = r.shape.elements.find(e => e.x === x + element.x && e.y === y + element.y);
77
+ if (!tile || tile.color !== element.color) {
78
+ match = false;
79
+ break;
80
+ }
81
+ }
82
+ if (match)
83
+ return false;
84
+ }
85
+ }
86
+ }
87
+ return true;
88
+ });
89
+ if (errorRegion) {
90
+ return {
91
+ state: State.Error,
92
+ positions: errorRegion.positions,
93
+ };
94
+ }
95
+ else {
96
+ return { state: complete ? State.Satisfied : State.Incomplete };
97
+ }
98
+ }
99
+ copyWith({ color, pattern, }) {
100
+ return new ContainsShapeRule(color ?? this.color, pattern ?? this.pattern);
101
+ }
102
+ withPattern(pattern) {
103
+ return this.copyWith({ pattern });
104
+ }
105
+ }
106
+ Object.defineProperty(ContainsShapeRule, "EXAMPLE_GRID_LIGHT", {
107
+ enumerable: true,
108
+ configurable: true,
109
+ writable: true,
110
+ value: Object.freeze(GridData.create(['nnnnn', 'nnnnn', 'wwwwn', 'nnnnn', 'nnnnn']))
111
+ });
112
+ Object.defineProperty(ContainsShapeRule, "EXAMPLE_GRID_DARK", {
113
+ enumerable: true,
114
+ configurable: true,
115
+ writable: true,
116
+ value: Object.freeze(GridData.create(['nnnnn', 'nnnnn', 'bbbbn', 'nnnnn', 'nnnnn']))
117
+ });
118
+ Object.defineProperty(ContainsShapeRule, "CONFIGS", {
119
+ enumerable: true,
120
+ configurable: true,
121
+ writable: true,
122
+ value: Object.freeze([
123
+ {
124
+ type: ConfigType.Color,
125
+ default: Color.Light,
126
+ allowGray: false,
127
+ field: 'color',
128
+ description: 'Color',
129
+ configurable: true,
130
+ },
131
+ {
132
+ type: ConfigType.Tile,
133
+ default: ContainsShapeRule.EXAMPLE_GRID_LIGHT,
134
+ resizable: true,
135
+ field: 'pattern',
136
+ description: 'Pattern',
137
+ configurable: true,
138
+ },
139
+ ])
140
+ });
141
+ Object.defineProperty(ContainsShapeRule, "SEARCH_VARIANTS", {
142
+ enumerable: true,
143
+ configurable: true,
144
+ writable: true,
145
+ value: [
146
+ new ContainsShapeRule(Color.Light, ContainsShapeRule.EXAMPLE_GRID_LIGHT).searchVariant(),
147
+ new ContainsShapeRule(Color.Dark, ContainsShapeRule.EXAMPLE_GRID_DARK).searchVariant(),
148
+ ]
149
+ });
150
+ export default ContainsShapeRule;
151
+ export const instance = new ContainsShapeRule(Color.Dark, GridData.create([]));
@@ -200,7 +200,12 @@ Object.defineProperty(MusicGridRule, "CONFIGS", {
200
200
  {
201
201
  type: ConfigType.NullableGrid,
202
202
  default: null,
203
- nonNullDefault: GridData.create(5, 4).addRule(new MusicGridRule([new ControlLine(0, 120, false, false, DEFAULT_SCALLE)], null)),
203
+ nonNullDefault: GridData.create([
204
+ 'wwwww',
205
+ 'wwwww',
206
+ 'wwwww',
207
+ 'wwwww',
208
+ ]).addRule(new MusicGridRule([new ControlLine(0, 120, false, false, DEFAULT_SCALLE)], null)),
204
209
  field: 'track',
205
210
  description: 'Track',
206
211
  configurable: true,
@@ -3,6 +3,7 @@ export { instance as CellCountPerZoneRule } from './cellCountPerZoneRule.js';
3
3
  export { instance as CellCountRule } from './cellCountRule.js';
4
4
  export { instance as CompletePatternRule } from './completePatternRule.js';
5
5
  export { instance as ConnectAllRule } from './connectAllRule.js';
6
+ export { instance as ContainsShapeRule } from './containsShapeRule.js';
6
7
  export { instance as CustomRule } from './customRule.js';
7
8
  export { instance as ForesightRule } from './foresightRule.js';
8
9
  export { instance as LyingSymbolRule } from './lyingSymbolRule.js';
@@ -7,6 +7,7 @@ export { instance as CellCountPerZoneRule } from './cellCountPerZoneRule.js';
7
7
  export { instance as CellCountRule } from './cellCountRule.js';
8
8
  export { instance as CompletePatternRule } from './completePatternRule.js';
9
9
  export { instance as ConnectAllRule } from './connectAllRule.js';
10
+ export { instance as ContainsShapeRule } from './containsShapeRule.js';
10
11
  export { instance as CustomRule } from './customRule.js';
11
12
  export { instance as ForesightRule } from './foresightRule.js';
12
13
  export { instance as LyingSymbolRule } from './lyingSymbolRule.js';
@@ -1,3 +1,4 @@
1
+ import GridData from './grid.js';
1
2
  import { Color, Position } from './primitives.js';
2
3
  import TileData from './tile.js';
3
4
  export interface ShapeElement {
@@ -15,3 +16,4 @@ export declare function tilesToShape(tiles: readonly (readonly TileData[])[]): S
15
16
  export declare function positionsToShape(positions: Position[], color: Color): Shape;
16
17
  export declare function getShapeVariants(shape: Shape): Shape[];
17
18
  export declare function normalizeShape(shape: Shape): Shape;
19
+ export declare function sanitizePatternGrid(pattern: GridData, tileMapper?: (tile: TileData) => TileData): GridData;
@@ -115,3 +115,13 @@ export function normalizeShape(shape) {
115
115
  const variants = getShapeVariants(shape);
116
116
  return variants.reduce((min, variant) => (compareShape(variant, min) < 0 ? variant : min), variants[0]);
117
117
  }
118
+ export function sanitizePatternGrid(pattern, tileMapper = t => t) {
119
+ return (pattern
120
+ // unlock all tiles
121
+ .withTiles(tiles => tiles.map(row => row.map(t => tileMapper(t.exists
122
+ ? t.withFixed(false)
123
+ : t.copyWith({ exists: true, color: Color.Gray, fixed: false })))))
124
+ // strip all symbols and rules
125
+ .withRules([])
126
+ .withSymbols(new Map()));
127
+ }
@@ -5,9 +5,13 @@ export default class AutoSolver extends Solver {
5
5
  readonly author = "various contributors";
6
6
  readonly description = "Automatically select the fastest solver based on supported instructions and environment.";
7
7
  readonly supportsCancellation = true;
8
- private gridSupportCache;
8
+ private static readonly nonAdditiveInstructions;
9
9
  isGridSupported(grid: GridData): boolean;
10
10
  isInstructionSupported(instructionId: string): boolean;
11
11
  protected isEnvironmentSupported(): Promise<boolean>;
12
+ private fillSolution;
13
+ private fixGrid;
14
+ private solveWithProgress;
15
+ private solveOne;
12
16
  solve(grid: GridData, abortSignal?: AbortSignal | undefined): AsyncGenerator<GridData | null>;
13
17
  }
@@ -1,6 +1,14 @@
1
+ import { Color, State } from '../../primitives.js';
2
+ import { instance as lyingSymbolInstance } from '../../rules/lyingSymbolRule.js';
3
+ import { instance as offByXInstance } from '../../rules/offByXRule.js';
4
+ import { instance as lotusInstance } from '../../symbols/lotusSymbol.js';
5
+ import { instance as galaxyInstance } from '../../symbols/galaxySymbol.js';
6
+ import { instance as wrapAroundInstance } from '../../rules/wrapAroundRule.js';
1
7
  import { allSolvers } from '../allSolvers.js';
2
8
  import Solver from '../solver.js';
3
- export default class AutoSolver extends Solver {
9
+ import UndercluedRule from '../../rules/undercluedRule.js';
10
+ import validateGrid from '../../validate.js';
11
+ class AutoSolver extends Solver {
4
12
  constructor() {
5
13
  super(...arguments);
6
14
  Object.defineProperty(this, "id", {
@@ -27,23 +35,15 @@ export default class AutoSolver extends Solver {
27
35
  writable: true,
28
36
  value: true
29
37
  });
30
- Object.defineProperty(this, "gridSupportCache", {
31
- enumerable: true,
32
- configurable: true,
33
- writable: true,
34
- value: null
35
- });
36
38
  }
37
39
  isGridSupported(grid) {
38
40
  for (const solver of allSolvers.values()) {
39
41
  if (solver.id === this.id)
40
42
  continue;
41
43
  if (solver.isGridSupported(grid)) {
42
- this.gridSupportCache = [grid, solver];
43
44
  return true;
44
45
  }
45
46
  }
46
- this.gridSupportCache = [grid, null];
47
47
  return false;
48
48
  }
49
49
  isInstructionSupported(instructionId) {
@@ -66,23 +66,118 @@ export default class AutoSolver extends Solver {
66
66
  }
67
67
  return false;
68
68
  }
69
- solve(grid, abortSignal) {
70
- let targetSolver = null;
71
- if (this.gridSupportCache && this.gridSupportCache[0] === grid) {
72
- targetSolver = this.gridSupportCache[1];
69
+ fillSolution(grid, solution) {
70
+ return grid.withTiles(tiles => {
71
+ return tiles.map((row, y) => row.map((tile, x) => {
72
+ if (!tile.exists || tile.fixed)
73
+ return tile;
74
+ const solutionTile = solution.tiles[y][x];
75
+ return tile.withColor(solutionTile.color);
76
+ }));
77
+ });
78
+ }
79
+ fixGrid(grid) {
80
+ return grid.withTiles(tiles => {
81
+ return tiles.map(row => row.map(tile => {
82
+ if (tile.fixed)
83
+ return tile;
84
+ return tile.withFixed(tile.color !== Color.Gray);
85
+ }));
86
+ });
87
+ }
88
+ async *solveWithProgress(solver, grid, progress, abortSignal) {
89
+ for await (const updatedGrid of solver.solve(progress, abortSignal)) {
90
+ if (abortSignal?.aborted)
91
+ return;
92
+ if (!updatedGrid)
93
+ return updatedGrid;
94
+ yield this.fillSolution(grid, updatedGrid);
73
95
  }
74
- else {
96
+ }
97
+ async solveOne(generator) {
98
+ // eslint-disable-next-line no-unreachable-loop
99
+ for await (const grid of generator) {
100
+ return grid;
101
+ }
102
+ return null;
103
+ }
104
+ async *solve(grid, abortSignal) {
105
+ if (!!grid.findRule(r => AutoSolver.nonAdditiveInstructions.has(r.id)) ||
106
+ !!grid.findSymbol(s => AutoSolver.nonAdditiveInstructions.has(s.id))) {
75
107
  for (const solver of allSolvers.values()) {
76
108
  if (solver.id === this.id)
77
109
  continue;
78
110
  if (solver.isGridSupported(grid)) {
79
- targetSolver = solver;
111
+ yield* solver.solve(grid, abortSignal);
112
+ return;
80
113
  }
81
114
  }
115
+ throw new Error('No solver supports the given grid');
82
116
  }
83
- if (targetSolver) {
84
- return targetSolver.solve(grid, abortSignal);
117
+ else {
118
+ let progressGrid = grid;
119
+ for (const solver of allSolvers.values()) {
120
+ if (solver.id === this.id)
121
+ continue;
122
+ if (solver.isGridSupported(progressGrid)) {
123
+ yield* this.solveWithProgress(solver, grid, progressGrid, abortSignal);
124
+ return;
125
+ }
126
+ else if (solver.isGridSupported(grid)) {
127
+ yield* solver.solve(grid, abortSignal);
128
+ return;
129
+ }
130
+ else {
131
+ let undercluedGrid = progressGrid
132
+ .withRules(rules => rules.filter(r => solver.isInstructionSupported(r.id)))
133
+ .withSymbols(symbols => {
134
+ for (const id of symbols.keys()) {
135
+ if (!solver.isInstructionSupported(id))
136
+ symbols.delete(id);
137
+ }
138
+ return symbols;
139
+ })
140
+ .addRule(new UndercluedRule());
141
+ if (!solver.isGridSupported(undercluedGrid)) {
142
+ // special case for solvers that support lotus and galaxy symbols but not dual-color placement
143
+ undercluedGrid = undercluedGrid.withSymbols(symbols => {
144
+ symbols.delete(lotusInstance.id);
145
+ symbols.delete(galaxyInstance.id);
146
+ return symbols;
147
+ });
148
+ }
149
+ if (!solver.isGridSupported(undercluedGrid))
150
+ continue;
151
+ const undercluedSolution = await this.solveOne(this.solveWithProgress(solver, progressGrid, undercluedGrid, abortSignal));
152
+ if (undercluedSolution === null)
153
+ continue;
154
+ if (undercluedSolution.getTileCount(true, false, Color.Gray) === 0) {
155
+ const result = this.fillSolution(grid, undercluedSolution);
156
+ if (validateGrid(result, null).final !== State.Error) {
157
+ yield result;
158
+ yield null;
159
+ return;
160
+ }
161
+ else {
162
+ yield null;
163
+ return;
164
+ }
165
+ }
166
+ progressGrid = this.fixGrid(undercluedSolution);
167
+ }
168
+ }
169
+ yield this.fillSolution(grid, progressGrid);
85
170
  }
86
- throw new Error('No solver supports the given grid');
87
171
  }
88
172
  }
173
+ Object.defineProperty(AutoSolver, "nonAdditiveInstructions", {
174
+ enumerable: true,
175
+ configurable: true,
176
+ writable: true,
177
+ value: new Set([
178
+ offByXInstance.id,
179
+ lyingSymbolInstance.id,
180
+ wrapAroundInstance.id,
181
+ ])
182
+ });
183
+ export default AutoSolver;
@@ -1,4 +1,5 @@
1
1
  import { ConfigType } from '../config.js';
2
+ import { move } from '../dataHelper.js';
2
3
  import GridData from '../grid.js';
3
4
  import { Color, ORIENTATIONS, Orientation, State, orientationToggle, } from '../primitives.js';
4
5
  import MultiEntrySymbol from './multiEntrySymbol.js';
@@ -71,11 +72,21 @@ class MyopiaSymbol extends MultiEntrySymbol {
71
72
  };
72
73
  const pos = { x: this.x, y: this.y };
73
74
  allDirections.forEach(direction => {
74
- grid.iterateDirectionAll(pos, direction, t => !t.exists || t.color === tile.color, () => {
75
+ let stopped = false;
76
+ grid.iterateDirectionAll(move(pos, direction), direction, t => {
77
+ if (!t.exists)
78
+ return true;
79
+ if (t.color === tile.color)
80
+ return true;
81
+ stopped = true;
82
+ return false;
83
+ }, () => {
75
84
  map[direction].min++;
76
85
  });
77
- let stopped = false;
78
- grid.iterateDirectionAll(pos, direction, t => {
86
+ if (!stopped && map[direction].min === 0)
87
+ map[direction].min = Number.MAX_SAFE_INTEGER;
88
+ stopped = false;
89
+ grid.iterateDirectionAll(move(pos, direction), direction, t => {
79
90
  if (!t.exists)
80
91
  return true;
81
92
  if (t.color === tile.color || t.color === Color.Gray)
package/dist/index.d.ts CHANGED
@@ -20,6 +20,7 @@ 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 ContainsShapeRule from './data/rules/containsShapeRule.js';
23
24
  import CustomRule from './data/rules/customRule.js';
24
25
  import ForesightRule from './data/rules/foresightRule.js';
25
26
  import { allRules } from './data/rules/index.js';
@@ -45,7 +46,7 @@ import GzipCompressor from './data/serializer/compressor/gzipCompressor.js';
45
46
  import StreamCompressor from './data/serializer/compressor/streamCompressor.js';
46
47
  import SerializerBase from './data/serializer/serializerBase.js';
47
48
  import SerializerV0 from './data/serializer/serializer_v0.js';
48
- import { getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape } from './data/shapes.js';
49
+ import { getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape } from './data/shapes.js';
49
50
  import { allSolvers } from './data/solver/allSolvers.js';
50
51
  import AutoSolver from './data/solver/auto/autoSolver.js';
51
52
  import BacktrackSolver from './data/solver/backtrack/backtrackSolver.js';
@@ -107,4 +108,4 @@ import ViewpointSymbol from './data/symbols/viewpointSymbol.js';
107
108
  import TileData from './data/tile.js';
108
109
  import TileConnections from './data/tileConnections.js';
109
110
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
110
- 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, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, WRAPPINGS, Wrapping, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, 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, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
111
+ 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, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, WRAPPINGS, Wrapping, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ContainsShapeRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, 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, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
package/dist/index.js CHANGED
@@ -23,6 +23,7 @@ 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 ContainsShapeRule from './data/rules/containsShapeRule.js';
26
27
  import CustomRule from './data/rules/customRule.js';
27
28
  import ForesightRule from './data/rules/foresightRule.js';
28
29
  import { allRules } from './data/rules/index.js';
@@ -48,7 +49,7 @@ import GzipCompressor from './data/serializer/compressor/gzipCompressor.js';
48
49
  import StreamCompressor from './data/serializer/compressor/streamCompressor.js';
49
50
  import SerializerBase from './data/serializer/serializerBase.js';
50
51
  import SerializerV0 from './data/serializer/serializer_v0.js';
51
- import { getShapeVariants, normalizeShape, positionsToShape, shapeEquals, tilesToShape } from './data/shapes.js';
52
+ import { getShapeVariants, normalizeShape, positionsToShape, sanitizePatternGrid, shapeEquals, tilesToShape } from './data/shapes.js';
52
53
  import { allSolvers } from './data/solver/allSolvers.js';
53
54
  import AutoSolver from './data/solver/auto/autoSolver.js';
54
55
  import BacktrackSolver from './data/solver/backtrack/backtrackSolver.js';
@@ -110,4 +111,4 @@ import ViewpointSymbol from './data/symbols/viewpointSymbol.js';
110
111
  import TileData from './data/tile.js';
111
112
  import TileConnections from './data/tileConnections.js';
112
113
  import validateGrid, { aggregateState, applyFinalOverrides } from './data/validate.js';
113
- 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, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, WRAPPINGS, Wrapping, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, getShapeVariants, normalizeShape, positionsToShape, 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, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
114
+ 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, Direction, MajorRule, Mode, ORIENTATIONS, Orientation, State, WRAPPINGS, Wrapping, directionToggle, orientationToggle, MetadataSchema, PuzzleSchema, BanPatternRule, CellCountPerZoneRule, CellCountRule, CompletePatternRule, ConnectAllRule, ContainsShapeRule, CustomRule, ForesightRule, allRules, LyingSymbolRule, ControlLine, Row, MusicGridRule, MysteryRule, OffByXRule, PerfectionRule, RegionAreaRule, RegionShapeRule, Rule, SameShapeRule, SymbolsPerRegionRule, UndercluedRule, UniqueShapeRule, WrapAroundRule, Serializer, Compressor, CompressorBase, DeflateCompressor, GzipCompressor, StreamCompressor, SerializerBase, SerializerV0, 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, allSymbols, LetterSymbol, LotusSymbol, MinesweeperSymbol, MultiEntrySymbol, MyopiaSymbol, NumberSymbol, Symbol, ViewpointSymbol, TileData, TileConnections, validateGrid, aggregateState, applyFinalOverrides, };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.10.2",
3
+ "version": "0.11.3",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",
@@ -58,6 +58,7 @@
58
58
  "events": "^3.3.0",
59
59
  "grilops": "^0.1.2",
60
60
  "lodash": "^4.17.21",
61
+ "logic-pad-solver-core": "^0.1.2",
61
62
  "z3-solver": "^4.13.0",
62
63
  "zod": "^3.24.1"
63
64
  },