@logic-pad/core 0.25.2 → 0.26.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.
Files changed (62) hide show
  1. package/assets/logic-core.global.d.ts +17 -104
  2. package/dist/src/data/grid.d.ts +1 -0
  3. package/dist/src/data/grid.js +10 -0
  4. package/dist/src/data/rules/noLoopsRule.js +123 -19
  5. package/dist/src/data/rules/offByXRule.js +2 -0
  6. package/dist/src/data/solver/allSolvers.js +0 -2
  7. package/dist/src/data/solver/auto/autoSolver.d.ts +2 -1
  8. package/dist/src/data/solver/auto/autoSolver.js +13 -17
  9. package/dist/src/data/solver/backtrack/backtrackSolver.d.ts +3 -1
  10. package/dist/src/data/solver/backtrack/backtrackSolver.js +2 -2
  11. package/dist/src/data/solver/backtrack/backtrackWorker.js +2 -2
  12. package/dist/src/data/solver/backtrack/data.d.ts +1 -0
  13. package/dist/src/data/solver/backtrack/data.js +43 -0
  14. package/dist/src/data/solver/backtrack/symbols/areaNumber.js +4 -1
  15. package/dist/src/data/solver/backtrack/symbols/letter.js +4 -1
  16. package/dist/src/data/solver/cspuz/cspuzSolver.d.ts +2 -1
  17. package/dist/src/data/solver/cspuz/cspuzSolver.js +46 -19
  18. package/dist/src/data/solver/solver.d.ts +2 -1
  19. package/dist/src/data/solver/solver.js +8 -7
  20. package/dist/src/data/solver/universal/universalSolver.d.ts +0 -1
  21. package/dist/src/data/solver/universal/universalSolver.js +0 -7
  22. package/dist/src/data/symbols/areaNumberSymbol.d.ts +1 -1
  23. package/dist/src/data/symbols/areaNumberSymbol.js +2 -0
  24. package/dist/src/data/symbols/everyLetterSymbol.js +2 -0
  25. package/dist/src/data/symbols/houseSymbol.d.ts +1 -1
  26. package/dist/src/data/symbols/houseSymbol.js +2 -0
  27. package/dist/src/data/symbols/letterSymbol.js +2 -0
  28. package/dist/src/data/symbols/numberSymbol.d.ts +1 -1
  29. package/dist/src/data/symbols/numberSymbol.js +4 -1
  30. package/dist/src/data/symbols/symbol.d.ts +5 -0
  31. package/dist/src/data/symbols/symbol.js +32 -0
  32. package/dist/src/index.d.ts +3 -16
  33. package/dist/src/index.js +3 -16
  34. package/package.json +1 -3
  35. package/dist/src/data/solver/z3/modules/areaNumberModule.d.ts +0 -9
  36. package/dist/src/data/solver/z3/modules/areaNumberModule.js +0 -27
  37. package/dist/src/data/solver/z3/modules/cellCountModule.d.ts +0 -9
  38. package/dist/src/data/solver/z3/modules/cellCountModule.js +0 -51
  39. package/dist/src/data/solver/z3/modules/connectAllModule.d.ts +0 -9
  40. package/dist/src/data/solver/z3/modules/connectAllModule.js +0 -24
  41. package/dist/src/data/solver/z3/modules/dartModule.d.ts +0 -9
  42. package/dist/src/data/solver/z3/modules/dartModule.js +0 -61
  43. package/dist/src/data/solver/z3/modules/index.d.ts +0 -3
  44. package/dist/src/data/solver/z3/modules/index.js +0 -10
  45. package/dist/src/data/solver/z3/modules/letterModule.d.ts +0 -9
  46. package/dist/src/data/solver/z3/modules/letterModule.js +0 -33
  47. package/dist/src/data/solver/z3/modules/modules.gen.d.ts +0 -8
  48. package/dist/src/data/solver/z3/modules/modules.gen.js +0 -12
  49. package/dist/src/data/solver/z3/modules/myopiaModule.d.ts +0 -9
  50. package/dist/src/data/solver/z3/modules/myopiaModule.js +0 -56
  51. package/dist/src/data/solver/z3/modules/regionAreaModule.d.ts +0 -9
  52. package/dist/src/data/solver/z3/modules/regionAreaModule.js +0 -40
  53. package/dist/src/data/solver/z3/modules/viewpointModule.d.ts +0 -9
  54. package/dist/src/data/solver/z3/modules/viewpointModule.js +0 -29
  55. package/dist/src/data/solver/z3/modules/z3Module.d.ts +0 -7
  56. package/dist/src/data/solver/z3/modules/z3Module.js +0 -3
  57. package/dist/src/data/solver/z3/utils.d.ts +0 -2
  58. package/dist/src/data/solver/z3/utils.js +0 -26
  59. package/dist/src/data/solver/z3/z3Solver.d.ts +0 -12
  60. package/dist/src/data/solver/z3/z3Solver.js +0 -123
  61. package/dist/src/data/solver/z3/z3SolverContext.d.ts +0 -13
  62. package/dist/src/data/solver/z3/z3SolverContext.js +0 -40
@@ -8,9 +8,7 @@
8
8
  declare global {
9
9
  // Generated by dts-bundle-generator v9.5.1
10
10
 
11
- import { RegionConstrainer, SymbolGrid } from 'grilops';
12
11
  import { PuzzleData as PuzzleData$1 } from 'logic-pad-solver-core';
13
- import { Optimize, Solver as Solver$1, Z3LowLevel } from 'z3-solver';
14
12
  import { z } from 'zod';
15
13
 
16
14
  interface Position$1 {
@@ -454,6 +452,11 @@ declare global {
454
452
  withX(x: number): this;
455
453
  withY(y: number): this;
456
454
  withPosition(x: number, y: number): this;
455
+ /**
456
+ * For symbols that can be placed between tiles, this method implements the default validation logic,
457
+ * which requires all tiles touching the symbol to be either gray or of the same color.
458
+ */
459
+ protected validateSubtilePlacement(grid: GridData): boolean;
457
460
  }
458
461
  export declare class TileData {
459
462
  readonly exists: boolean;
@@ -746,6 +749,7 @@ declare global {
746
749
  get isSingleton(): boolean;
747
750
  }
748
751
  export declare const NEIGHBOR_OFFSETS: Position$1[];
752
+ export declare const NEIGHBOR_OFFSETS_8: Position$1[];
749
753
  export declare class GridData {
750
754
  readonly width: number;
751
755
  readonly height: number;
@@ -2387,7 +2391,7 @@ declare global {
2387
2391
  *
2388
2392
  * @param instructionId The unique identifier of the instruction.
2389
2393
  */
2390
- isInstructionSupported(instructionId: string): boolean;
2394
+ isInstructionSupported(_grid: GridData, instruction: Instruction): boolean;
2391
2395
  /**
2392
2396
  * Check if the solver supports the given grid. This methid is frequently called when the user changes the grid, and
2393
2397
  * the result is used to enable or disable the "Solve" button.
@@ -2409,7 +2413,7 @@ declare global {
2409
2413
  readonly supportsCancellation = true;
2410
2414
  private static readonly nonAdditiveInstructions;
2411
2415
  isGridSupported(grid: GridData): boolean;
2412
- isInstructionSupported(instructionId: string): boolean;
2416
+ isInstructionSupported(grid: GridData, instruction: Instruction): boolean;
2413
2417
  protected isEnvironmentSupported(): Promise<boolean>;
2414
2418
  private fillSolution;
2415
2419
  private fixGrid;
@@ -2436,7 +2440,7 @@ declare global {
2436
2440
  readonly description =
2437
2441
  'Solves puzzles pretty fast using backtracking with optimizations. Support most rules and symbols (including underclued).';
2438
2442
  protected createWorker(): Worker;
2439
- isInstructionSupported(instructionId: string): boolean;
2443
+ isInstructionSupported(_grid: GridData, instruction: Instruction): boolean;
2440
2444
  }
2441
2445
  export declare enum BTTile {
2442
2446
  Empty = 0,
@@ -2493,6 +2497,10 @@ declare global {
2493
2497
  pos: Position$1,
2494
2498
  score?: number | undefined
2495
2499
  ): CheckResult;
2500
+ export declare function checkSubtilePlacement(
2501
+ grid: BTGridData,
2502
+ pos: Position$1
2503
+ ): CheckResult | false | undefined;
2496
2504
  export declare class BanPatternBTModule extends BTModule {
2497
2505
  instr: BanPatternRule;
2498
2506
  constructor(instr: BanPatternRule);
@@ -2554,7 +2562,7 @@ declare global {
2554
2562
  abstract countTiles(grid: GridData): {
2555
2563
  completed: number;
2556
2564
  possible: number;
2557
- };
2565
+ } | null;
2558
2566
  validateSymbol(grid: GridData): State;
2559
2567
  withNumber(number: number): this;
2560
2568
  }
@@ -2577,7 +2585,7 @@ declare global {
2577
2585
  countTiles(grid: GridData): {
2578
2586
  completed: number;
2579
2587
  possible: number;
2580
- };
2588
+ } | null;
2581
2589
  copyWith({
2582
2590
  x,
2583
2591
  y,
@@ -2965,7 +2973,7 @@ declare global {
2965
2973
  'A blazingly fast WebAssembly solver that supports most rules and symbols (including underclued).';
2966
2974
  protected createWorker(): Worker;
2967
2975
  isGridSupported(grid: GridData): boolean;
2968
- isInstructionSupported(instructionId: string): boolean;
2976
+ isInstructionSupported(grid: GridData, instruction: Instruction): boolean;
2969
2977
  isEnvironmentSupported(): Promise<boolean>;
2970
2978
  }
2971
2979
  export declare function gridToJson(grid: GridData): PuzzleData$1;
@@ -2975,101 +2983,6 @@ declare global {
2975
2983
  readonly description =
2976
2984
  'A backtracking solver that supports all rules and symbols (including underclued) but is less optimized.';
2977
2985
  protected createWorker(): Worker;
2978
- isInstructionSupported(instructionId: string): boolean;
2979
- }
2980
- export declare class Z3SolverContext<
2981
- Name extends string,
2982
- const Core extends Solver$1<Name> | Optimize<Name> =
2983
- | Solver$1<Name>
2984
- | Optimize<Name>,
2985
- > {
2986
- readonly grid: SymbolGrid<Name, Core>;
2987
- private _regionConstrainer;
2988
- constructor(grid: SymbolGrid<Name, Core>);
2989
- get solver(): Core;
2990
- get lattice(): import('grilops').Lattice;
2991
- get symbolSet(): import('grilops').SymbolSet;
2992
- get ctx(): import('z3-solver').Context<Name>;
2993
- get z3(): Z3LowLevel['Z3'];
2994
- get regionConstrainer(): RegionConstrainer<Name, Core>;
2995
- }
2996
- export declare abstract class Z3Module {
2997
- abstract get id(): string;
2998
- abstract encode<Name extends string>(
2999
- grid: GridData,
3000
- ctx: Z3SolverContext<Name>
3001
- ): void;
3002
- }
3003
- export declare class AreaNumberModule extends Z3Module {
3004
- readonly id: string;
3005
- encode<Name extends string>(
3006
- grid: GridData,
3007
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3008
- ): void;
3009
- }
3010
- export declare class CellCountModule extends Z3Module {
3011
- readonly id: string;
3012
- encode<Name extends string>(
3013
- grid: GridData,
3014
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3015
- ): void;
3016
- }
3017
- export declare class ConnectAllModule extends Z3Module {
3018
- readonly id: string;
3019
- encode<Name extends string>(
3020
- grid: GridData,
3021
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3022
- ): void;
3023
- }
3024
- export declare class DartModule extends Z3Module {
3025
- readonly id: string;
3026
- encode<Name extends string>(
3027
- grid: GridData,
3028
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3029
- ): void;
3030
- }
3031
- export declare const allZ3Modules: Map<string, Z3Module>;
3032
- export declare class LetterModule extends Z3Module {
3033
- readonly id: string;
3034
- encode<Name extends string>(
3035
- grid: GridData,
3036
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3037
- ): void;
3038
- }
3039
- export declare class MyopiaModule extends Z3Module {
3040
- readonly id: string;
3041
- encode<Name extends string>(
3042
- grid: GridData,
3043
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3044
- ): void;
3045
- }
3046
- export declare class RegionAreaModule extends Z3Module {
3047
- readonly id: string;
3048
- encode<Name extends string>(
3049
- grid: GridData,
3050
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3051
- ): void;
3052
- }
3053
- export declare class ViewpointModule extends Z3Module {
3054
- readonly id: string;
3055
- encode<Name extends string>(
3056
- grid: GridData,
3057
- ctx: Z3SolverContext<Name, Solver$1<Name> | Optimize<Name>>
3058
- ): void;
3059
- }
3060
- export declare function convertDirection(
3061
- direction: Orientation | Direction
3062
- ): import('grilops').Direction;
3063
- export declare class Z3Solver extends Solver {
3064
- readonly id = 'z3';
3065
- readonly author = 'Lysine';
3066
- readonly description =
3067
- '(Obsolete) A WebAssembly solver that supports a limited set of rules and symbols.';
3068
- readonly supportsCancellation = false;
3069
- protected isEnvironmentSupported(): Promise<boolean>;
3070
- solve(grid: GridData): AsyncGenerator<GridData | null>;
3071
- isInstructionSupported(instructionId: string): boolean;
3072
- isGridSupported(grid: GridData): boolean;
3073
2986
  }
3074
2987
  export declare abstract class CustomSymbol
3075
2988
  extends Symbol$1
@@ -3281,7 +3194,7 @@ declare global {
3281
3194
  countTiles(grid: GridData): {
3282
3195
  completed: number;
3283
3196
  possible: number;
3284
- };
3197
+ } | null;
3285
3198
  copyWith({
3286
3199
  x,
3287
3200
  y,
@@ -10,6 +10,7 @@ import UndercluedRule from './rules/undercluedRule.js';
10
10
  import GridZones from './gridZones.js';
11
11
  import WrapAroundRule from './rules/wrapAroundRule.js';
12
12
  export declare const NEIGHBOR_OFFSETS: Position[];
13
+ export declare const NEIGHBOR_OFFSETS_8: Position[];
13
14
  export default class GridData {
14
15
  readonly width: number;
15
16
  readonly height: number;
@@ -12,6 +12,16 @@ export const NEIGHBOR_OFFSETS = [
12
12
  { x: 0, y: -1 },
13
13
  { x: 0, y: 1 },
14
14
  ];
15
+ export const NEIGHBOR_OFFSETS_8 = [
16
+ { x: -1, y: 0 },
17
+ { x: 1, y: 0 },
18
+ { x: 0, y: -1 },
19
+ { x: 0, y: 1 },
20
+ { x: -1, y: -1 },
21
+ { x: 1, y: -1 },
22
+ { x: -1, y: 1 },
23
+ { x: 1, y: 1 },
24
+ ];
15
25
  export default class GridData {
16
26
  width;
17
27
  height;
@@ -1,5 +1,5 @@
1
1
  import { ConfigType } from '../config.js';
2
- import GridData, { NEIGHBOR_OFFSETS } from '../grid.js';
2
+ import GridData, { NEIGHBOR_OFFSETS, NEIGHBOR_OFFSETS_8 } from '../grid.js';
3
3
  import { array } from '../dataHelper.js';
4
4
  import { Color, State } from '../primitives.js';
5
5
  import Rule from './rule.js';
@@ -60,48 +60,152 @@ export default class NoLoopsRule extends Rule {
60
60
  return NoLoopsRule.SEARCH_VARIANTS;
61
61
  }
62
62
  validateGrid(grid) {
63
+ // wrap-around grids require special consideration because the "islands" assumption of the
64
+ // algorithm below does not hold
65
+ if (grid.wrapAround.value) {
66
+ const visited = array(grid.width, grid.height, (i, j) => {
67
+ const tile = grid.getTile(i, j);
68
+ return (!tile.exists ||
69
+ (tile.color !== this.color && tile.color !== Color.Gray));
70
+ });
71
+ while (true) {
72
+ const seed = grid.find((tile, x, y) => !visited[y][x] && tile.color === this.color);
73
+ if (!seed)
74
+ break;
75
+ let invalid = false;
76
+ const positions = [];
77
+ const stack = [[seed, null]];
78
+ while (stack.length > 0) {
79
+ const [{ x, y }, from] = stack.pop();
80
+ const { x: arrX, y: arrY } = grid.toArrayCoordinates(x, y);
81
+ positions.push({ x: arrX, y: arrY });
82
+ if (visited[arrY][arrX]) {
83
+ invalid = true;
84
+ continue;
85
+ }
86
+ visited[arrY][arrX] = true;
87
+ for (const offset of NEIGHBOR_OFFSETS) {
88
+ if (-offset.x === from?.x && -offset.y === from?.y)
89
+ continue;
90
+ const next = { x: x + offset.x, y: y + offset.y };
91
+ if (grid.isPositionValid(next.x, next.y)) {
92
+ const nextTile = grid.getTile(next.x, next.y);
93
+ if (nextTile.exists && nextTile.color === this.color)
94
+ stack.push([next, offset]);
95
+ }
96
+ }
97
+ }
98
+ if (invalid) {
99
+ return {
100
+ state: State.Error,
101
+ positions,
102
+ };
103
+ }
104
+ }
105
+ return {
106
+ state: visited.some(row => row.some(v => !v))
107
+ ? State.Incomplete
108
+ : State.Satisfied,
109
+ };
110
+ }
111
+ // special handling for 2x2 loops
112
+ for (let y = 0; y < grid.height; y++) {
113
+ for (let x = 0; x < grid.width; x++) {
114
+ const tlTile = grid.getTile(x, y);
115
+ const trTile = grid.getTile(x + 1, y);
116
+ const blTile = grid.getTile(x, y + 1);
117
+ const brTile = grid.getTile(x + 1, y + 1);
118
+ if (tlTile.exists &&
119
+ tlTile.color === this.color &&
120
+ trTile.exists &&
121
+ trTile.color === this.color &&
122
+ blTile.exists &&
123
+ blTile.color === this.color &&
124
+ brTile.exists &&
125
+ brTile.color === this.color) {
126
+ const positions = [
127
+ grid.toArrayCoordinates(x, y),
128
+ grid.toArrayCoordinates(x + 1, y),
129
+ grid.toArrayCoordinates(x, y + 1),
130
+ grid.toArrayCoordinates(x + 1, y + 1),
131
+ ];
132
+ return {
133
+ state: State.Error,
134
+ positions,
135
+ };
136
+ }
137
+ }
138
+ }
139
+ // general case for non-wrap-around grids: a loop must form an elcosed island that does not touch the grid edge
63
140
  const visited = array(grid.width, grid.height, (i, j) => {
64
141
  const tile = grid.getTile(i, j);
65
- return (!tile.exists || (tile.color !== this.color && tile.color !== Color.Gray));
142
+ return tile.exists && tile.color === this.color;
66
143
  });
144
+ const shape = array(grid.width, grid.height, () => false);
145
+ let complete = true;
67
146
  while (true) {
68
- const seed = grid.find((tile, x, y) => !visited[y][x] && tile.color === this.color);
147
+ const seed = grid.find((tile, x, y) => !visited[y][x] && (!tile.exists || tile.color !== this.color));
148
+ shape.forEach(row => row.fill(false));
69
149
  if (!seed)
70
150
  break;
71
- let invalid = false;
72
- const positions = [];
73
- const stack = [[seed, null]];
151
+ let isIsland = true;
152
+ const stack = [seed];
74
153
  while (stack.length > 0) {
75
- const [{ x, y }, from] = stack.pop();
154
+ const { x, y } = stack.pop();
76
155
  const { x: arrX, y: arrY } = grid.toArrayCoordinates(x, y);
77
- positions.push({ x, y });
156
+ const tile = grid.getTile(x, y);
157
+ if (tile.exists && tile.color === Color.Gray) {
158
+ complete = false;
159
+ }
78
160
  if (visited[arrY][arrX]) {
79
- invalid = true;
80
161
  continue;
81
162
  }
82
163
  visited[arrY][arrX] = true;
83
- for (const offset of NEIGHBOR_OFFSETS) {
84
- if (-offset.x === from?.x && -offset.y === from?.y)
85
- continue;
164
+ for (const offset of NEIGHBOR_OFFSETS_8) {
86
165
  const next = { x: x + offset.x, y: y + offset.y };
166
+ const arrPos = grid.toArrayCoordinates(next.x, next.y);
87
167
  if (grid.isPositionValid(next.x, next.y)) {
88
168
  const nextTile = grid.getTile(next.x, next.y);
89
- if (nextTile.exists && nextTile.color === this.color)
90
- stack.push([next, offset]);
169
+ shape[arrPos.y][arrPos.x] = true;
170
+ if (!nextTile.exists || nextTile.color !== this.color) {
171
+ stack.push(arrPos);
172
+ }
173
+ }
174
+ else {
175
+ isIsland = false;
91
176
  }
92
177
  }
93
178
  }
94
- if (invalid) {
179
+ if (isIsland) {
180
+ const loopPositions = [];
181
+ for (let y = 0; y < grid.height; y++) {
182
+ for (let x = 0; x < grid.width; x++) {
183
+ if (shape[y][x]) {
184
+ if (x > 0 &&
185
+ y > 0 &&
186
+ x < grid.width - 1 &&
187
+ y < grid.height - 1 &&
188
+ shape[y][x - 1] &&
189
+ shape[y - 1][x] &&
190
+ shape[y][x + 1] &&
191
+ shape[y + 1][x] &&
192
+ shape[y - 1][x - 1] &&
193
+ shape[y - 1][x + 1] &&
194
+ shape[y + 1][x - 1] &&
195
+ shape[y + 1][x + 1])
196
+ continue;
197
+ loopPositions.push({ x, y });
198
+ }
199
+ }
200
+ }
95
201
  return {
96
202
  state: State.Error,
97
- positions,
203
+ positions: loopPositions,
98
204
  };
99
205
  }
100
206
  }
101
207
  return {
102
- state: visited.some(row => row.some(v => !v))
103
- ? State.Incomplete
104
- : State.Satisfied,
208
+ state: complete ? State.Satisfied : State.Incomplete,
105
209
  };
106
210
  }
107
211
  copyWith({ color }) {
@@ -90,6 +90,8 @@ export default class OffByXRule extends Rule {
90
90
  onSymbolValidation(grid, symbol, _validator) {
91
91
  if (symbol instanceof NumberSymbol) {
92
92
  const counts = symbol.countTiles(grid);
93
+ if (counts === null)
94
+ return State.Error;
93
95
  if (counts.completed > symbol.number + this.number ||
94
96
  counts.possible < symbol.number - this.number ||
95
97
  (counts.completed > symbol.number - this.number &&
@@ -1,6 +1,5 @@
1
1
  import UniversalSolver from './universal/universalSolver.js';
2
2
  import BacktrackSolver from './backtrack/backtrackSolver.js';
3
- import Z3Solver from './z3/z3Solver.js';
4
3
  import CspuzSolver from './cspuz/cspuzSolver.js';
5
4
  import AutoSolver from './auto/autoSolver.js';
6
5
  const allSolvers = new Map();
@@ -11,5 +10,4 @@ register(new AutoSolver());
11
10
  register(new CspuzSolver());
12
11
  register(new BacktrackSolver());
13
12
  register(new UniversalSolver());
14
- register(new Z3Solver());
15
13
  export { allSolvers };
@@ -1,5 +1,6 @@
1
1
  import GridData from '../../grid.js';
2
2
  import Solver from '../solver.js';
3
+ import Instruction from '../../instruction.js';
3
4
  export default class AutoSolver extends Solver {
4
5
  readonly id = "auto";
5
6
  readonly author = "various contributors";
@@ -7,7 +8,7 @@ export default class AutoSolver extends Solver {
7
8
  readonly supportsCancellation = true;
8
9
  private static readonly nonAdditiveInstructions;
9
10
  isGridSupported(grid: GridData): boolean;
10
- isInstructionSupported(instructionId: string): boolean;
11
+ isInstructionSupported(grid: GridData, instruction: Instruction): boolean;
11
12
  protected isEnvironmentSupported(): Promise<boolean>;
12
13
  private fillSolution;
13
14
  private fixGrid;
@@ -1,10 +1,10 @@
1
1
  import { Color, State } from '../../primitives.js';
2
2
  import { instance as lyingSymbolInstance } from '../../rules/lyingSymbolRule.js';
3
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
4
  import { instance as wrapAroundInstance } from '../../rules/wrapAroundRule.js';
7
5
  import { instance as symbolsPerRegionInstance } from '../../rules/symbolsPerRegionRule.js';
6
+ import { instance as areaNumberInstance } from '../../symbols/areaNumberSymbol.js';
7
+ import { instance as letterInstance } from '../../symbols/letterSymbol.js';
8
8
  import { allSolvers } from '../allSolvers.js';
9
9
  import Solver from '../solver.js';
10
10
  import UndercluedRule from '../../rules/undercluedRule.js';
@@ -30,11 +30,11 @@ export default class AutoSolver extends Solver {
30
30
  }
31
31
  return false;
32
32
  }
33
- isInstructionSupported(instructionId) {
33
+ isInstructionSupported(grid, instruction) {
34
34
  for (const solver of allSolvers.values()) {
35
35
  if (solver.id === this.id)
36
36
  continue;
37
- if (solver.isInstructionSupported(instructionId)) {
37
+ if (solver.isInstructionSupported(grid, instruction)) {
38
38
  return true;
39
39
  }
40
40
  }
@@ -113,24 +113,20 @@ export default class AutoSolver extends Solver {
113
113
  return;
114
114
  }
115
115
  else {
116
- let undercluedGrid = progressGrid
117
- .withRules(rules => rules.filter(r => solver.isInstructionSupported(r.id)))
116
+ const undercluedGrid = progressGrid
117
+ .withRules(rules => rules.filter(r => solver.isInstructionSupported(progressGrid, r)))
118
118
  .withSymbols(symbols => {
119
- for (const id of symbols.keys()) {
120
- if (!solver.isInstructionSupported(id))
121
- symbols.delete(id);
119
+ for (const [id, symbolList] of symbols.entries()) {
120
+ symbols.set(id, symbolList.filter(symbol =>
121
+ // special handling: do not delete area number and letter symbols as they can be solved
122
+ // underclued even if the solver doesn't fully support them
123
+ symbol.id === areaNumberInstance.id ||
124
+ symbol.id === letterInstance.id ||
125
+ solver.isInstructionSupported(progressGrid, symbol)));
122
126
  }
123
127
  return symbols;
124
128
  })
125
129
  .addRule(new UndercluedRule());
126
- if (!solver.isGridSupported(undercluedGrid)) {
127
- // special case for solvers that support lotus and galaxy symbols but not dual-color placement
128
- undercluedGrid = undercluedGrid.withSymbols(symbols => {
129
- symbols.delete(lotusInstance.id);
130
- symbols.delete(galaxyInstance.id);
131
- return symbols;
132
- });
133
- }
134
130
  if (!solver.isGridSupported(undercluedGrid))
135
131
  continue;
136
132
  const undercluedSolution = await this.solveOne(this.solveWithProgress(solver, progressGrid, undercluedGrid, abortSignal));
@@ -1,9 +1,11 @@
1
1
  import EventIteratingSolver from '../eventIteratingSolver.js';
2
+ import Instruction from '../../instruction.js';
3
+ import GridData from '../../grid.js';
2
4
  export default class BacktrackSolver extends EventIteratingSolver {
3
5
  private static readonly supportedInstrs;
4
6
  readonly id = "backtrack";
5
7
  readonly author = "ALaggyDev";
6
8
  readonly description = "Solves puzzles pretty fast using backtracking with optimizations. Support most rules and symbols (including underclued).";
7
9
  protected createWorker(): Worker;
8
- isInstructionSupported(instructionId: string): boolean;
10
+ isInstructionSupported(_grid: GridData, instruction: Instruction): boolean;
9
11
  }
@@ -46,7 +46,7 @@ export default class BacktrackSolver extends EventIteratingSolver {
46
46
  type: 'module',
47
47
  });
48
48
  }
49
- isInstructionSupported(instructionId) {
50
- return BacktrackSolver.supportedInstrs.includes(instructionId);
49
+ isInstructionSupported(_grid, instruction) {
50
+ return BacktrackSolver.supportedInstrs.includes(instruction.id);
51
51
  }
52
52
  }
@@ -2,6 +2,7 @@ import { array } from '../../dataHelper.js';
2
2
  import { Color } from '../../primitives.js';
3
3
  import { instance as banPatternInstance, } from '../../rules/banPatternRule.js';
4
4
  import { instance as cellCountInstance, } from '../../rules/cellCountRule.js';
5
+ import { instance as connectAllInstance, } from '../../rules/connectAllRule.js';
5
6
  import { instance as regionAreaInstance, } from '../../rules/regionAreaRule.js';
6
7
  import { instance as sameShapeInstance, } from '../../rules/sameShapeRule.js';
7
8
  import { instance as symbolsPerRegionInstance, } from '../../rules/symbolsPerRegionRule.js';
@@ -17,7 +18,6 @@ import { instance as minesweeperInstance, } from '../../symbols/minesweeperSymbo
17
18
  import { instance as focusInstance, } from '../../symbols/focusSymbol.js';
18
19
  import { instance as myopiaInstance, } from '../../symbols/myopiaSymbol.js';
19
20
  import { instance as viewpointInstance, } from '../../symbols/viewpointSymbol.js';
20
- import { instance as connectAllInstance } from '../z3/modules/connectAllModule.js';
21
21
  import { BTGridData, BTTile } from './data.js';
22
22
  import BanPatternBTModule from './rules/banPattern.js';
23
23
  import CellCountBTModule from './rules/cellCount.js';
@@ -104,7 +104,7 @@ function translateToBTGridData(grid) {
104
104
  }
105
105
  else if (rule.id === symbolsPerRegionInstance.id) {
106
106
  const allSymbols = [];
107
- grid.symbols.forEach(symbols => allSymbols.push(...symbols));
107
+ grid.symbols.forEach(symbols => allSymbols.push(...symbols.filter(symbol => symbol.necessaryForCompletion)));
108
108
  module = new SymbolsPerRegionBTModule(rule, grid.width, grid.height, allSymbols);
109
109
  }
110
110
  else if (rule.id === cellCountInstance.id) {
@@ -44,3 +44,4 @@ export default abstract class BTModule {
44
44
  export declare function getOppositeColor(color: BTColor): BTColor;
45
45
  export declare function colorToBTTile(color: Color): BTTile;
46
46
  export declare function createOneTileResult(grid: BTGridData, pos: Position, score?: number | undefined): CheckResult;
47
+ export declare function checkSubtilePlacement(grid: BTGridData, pos: Position): CheckResult | false | undefined;
@@ -106,3 +106,46 @@ export function createOneTileResult(grid, pos, score = 1) {
106
106
  const ratings = [{ pos, score }];
107
107
  return { tilesNeedCheck, ratings };
108
108
  }
109
+ export function checkSubtilePlacement(grid, pos) {
110
+ const minX = Math.floor(pos.x);
111
+ const minY = Math.floor(pos.y);
112
+ if (minX === pos.x && minY === pos.y)
113
+ return undefined;
114
+ const maxX = Math.ceil(pos.x);
115
+ const maxY = Math.ceil(pos.y);
116
+ let color = null;
117
+ let complete = true;
118
+ for (let i = 0; i < 4; i++) {
119
+ const x = i % 2 === 0 ? minX : maxX;
120
+ const y = i < 2 ? minY : maxY;
121
+ if (!grid.isInBound(x, y))
122
+ return false;
123
+ const tile = grid.getTile(x, y);
124
+ if (tile === BTTile.NonExist)
125
+ return false;
126
+ if (tile !== BTTile.Empty) {
127
+ if (color !== null && tile !== color)
128
+ return false;
129
+ color = tile;
130
+ }
131
+ else {
132
+ complete = false;
133
+ }
134
+ }
135
+ if (complete) {
136
+ return undefined;
137
+ }
138
+ else {
139
+ const tilesNeedCheck = IntArray2D.create(grid.width, grid.height);
140
+ const ratings = [];
141
+ for (let i = 0; i < 4; i++) {
142
+ const x = i % 2 === 0 ? minX : maxX;
143
+ const y = i < 2 ? minY : maxY;
144
+ if (grid.getTile(x, y) === BTTile.Empty) {
145
+ tilesNeedCheck.set(x, y, 1);
146
+ ratings.push({ pos: { x, y }, score: 1 });
147
+ }
148
+ }
149
+ return { tilesNeedCheck, ratings };
150
+ }
151
+ }
@@ -1,4 +1,4 @@
1
- import BTModule, { BTTile, IntArray2D, createOneTileResult, } from '../data.js';
1
+ import BTModule, { BTTile, checkSubtilePlacement, IntArray2D, createOneTileResult, } from '../data.js';
2
2
  export default class AreaNumberBTModule extends BTModule {
3
3
  instr;
4
4
  constructor(instr) {
@@ -6,6 +6,9 @@ export default class AreaNumberBTModule extends BTModule {
6
6
  this.instr = instr;
7
7
  }
8
8
  checkGlobal(grid) {
9
+ const checkResult = checkSubtilePlacement(grid, this.instr);
10
+ if (checkResult !== undefined)
11
+ return checkResult;
9
12
  const thisX = Math.floor(this.instr.x);
10
13
  const thisY = Math.floor(this.instr.y);
11
14
  const tile = grid.getTile(thisX, thisY);
@@ -1,4 +1,4 @@
1
- import BTModule, { BTTile, IntArray2D, } from '../data.js';
1
+ import BTModule, { BTTile, checkSubtilePlacement, IntArray2D, } from '../data.js';
2
2
  export default class LetterBTModule extends BTModule {
3
3
  letters;
4
4
  letterGrid;
@@ -23,6 +23,9 @@ export default class LetterBTModule extends BTModule {
23
23
  const visited = IntArray2D.create(grid.width, grid.height);
24
24
  for (let id = 0; id < this.letters.length; id++) {
25
25
  for (const symbol of this.letters[id]) {
26
+ const checkResult = checkSubtilePlacement(grid, symbol);
27
+ if (checkResult !== undefined)
28
+ return checkResult;
26
29
  const symbolX = Math.floor(symbol.x);
27
30
  const symbolY = Math.floor(symbol.y);
28
31
  if (grid.getTile(symbolX, symbolY) === BTTile.Empty)
@@ -1,5 +1,6 @@
1
1
  import EventIteratingSolver from '../eventIteratingSolver.js';
2
2
  import GridData from '../../grid.js';
3
+ import Instruction from '../../instruction.js';
3
4
  export default class CspuzSolver extends EventIteratingSolver {
4
5
  private static readonly supportedInstrs;
5
6
  readonly id = "cspuz";
@@ -7,6 +8,6 @@ export default class CspuzSolver extends EventIteratingSolver {
7
8
  readonly description = "A blazingly fast WebAssembly solver that supports most rules and symbols (including underclued).";
8
9
  protected createWorker(): Worker;
9
10
  isGridSupported(grid: GridData): boolean;
10
- isInstructionSupported(instructionId: string): boolean;
11
+ isInstructionSupported(grid: GridData, instruction: Instruction): boolean;
11
12
  isEnvironmentSupported(): Promise<boolean>;
12
13
  }