@logic-pad/core 0.2.0 → 0.2.2

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.
@@ -2011,7 +2011,6 @@ declare global {
2011
2011
  get explanation(): string;
2012
2012
  get configs(): readonly AnyConfig[] | null;
2013
2013
  createExampleGrid(): GridData;
2014
- private getColor;
2015
2014
  private deltaCoordinate;
2016
2015
  validateSymbol(grid: GridData): State;
2017
2016
  copyWith({ x, y }: { x?: number; y?: number }): this;
@@ -2020,6 +2019,8 @@ declare global {
2020
2019
  export declare abstract class DirectionLinkerBTModule extends BTModule {
2021
2020
  instr: DirectionLinkerSymbol;
2022
2021
  constructor(instr: DirectionLinkerSymbol);
2022
+ private initialPositions;
2023
+ private getInitialPositions;
2023
2024
  checkGlobal(grid: BTGridData): CheckResult | false;
2024
2025
  protected abstract movePos(
2025
2026
  grid: BTGridData,
@@ -2127,6 +2128,8 @@ declare global {
2127
2128
  x: number,
2128
2129
  y: number
2129
2130
  ): Position$1 | null;
2131
+ private getTileSafe;
2132
+ checkGlobal(grid: BTGridData): false | CheckResult;
2130
2133
  }
2131
2134
  export declare class MinesweeperSymbol extends NumberSymbol {
2132
2135
  private static readonly CONFIGS;
@@ -5893,6 +5896,7 @@ declare global {
5893
5896
  readonly x: number;
5894
5897
  readonly y: number;
5895
5898
  readonly color: Color;
5899
+ readonly revealLocation: boolean;
5896
5900
  private static readonly CONFIGS;
5897
5901
  private static readonly EXAMPLE_GRID;
5898
5902
  /**
@@ -5901,8 +5905,9 @@ declare global {
5901
5905
  * @param x - The x-coordinate of the symbol.
5902
5906
  * @param y - The y-coordinate of the symbol.
5903
5907
  * @param color - The target color of the cell.
5908
+ * @param revealLocation - Whether to reveal the location of the symbol.
5904
5909
  */
5905
- constructor(x: number, y: number, color: Color);
5910
+ constructor(x: number, y: number, color: Color, revealLocation?: boolean);
5906
5911
  get id(): string;
5907
5912
  get explanation(): string;
5908
5913
  get configs(): readonly AnyConfig[] | null;
@@ -5916,8 +5921,19 @@ declare global {
5916
5921
  symbol: Symbol$1,
5917
5922
  editing: boolean
5918
5923
  ): boolean;
5919
- copyWith({ x, y, color }: { x?: number; y?: number; color?: Color }): this;
5924
+ copyWith({
5925
+ x,
5926
+ y,
5927
+ color,
5928
+ revealLocation,
5929
+ }: {
5930
+ x?: number;
5931
+ y?: number;
5932
+ color?: Color;
5933
+ revealLocation?: boolean;
5934
+ }): this;
5920
5935
  withColor(color: Color): this;
5936
+ withRevealLocation(revealLocation: boolean): this;
5921
5937
  }
5922
5938
  export declare const allSymbols: Map<string, Symbol$1>;
5923
5939
  export declare function aggregateState(
@@ -50,7 +50,7 @@ class BacktrackSolver extends Solver {
50
50
  });
51
51
  worker.addEventListener('error', (e) => {
52
52
  alert(`Error while solving!\n${e.message}`);
53
- fail(new Error(e.message));
53
+ fail(e);
54
54
  });
55
55
  });
56
56
  for await (const solution of iterator) {
@@ -4,6 +4,8 @@ import BTModule, { BTGridData, CheckResult } from '../data.js';
4
4
  export default abstract class DirectionLinkerBTModule extends BTModule {
5
5
  instr: DirectionLinkerSymbol;
6
6
  constructor(instr: DirectionLinkerSymbol);
7
+ private initialPositions;
8
+ private getInitialPositions;
7
9
  checkGlobal(grid: BTGridData): CheckResult | false;
8
10
  protected abstract movePos(grid: BTGridData, x: number, y: number): Position | null;
9
11
  }
@@ -1,4 +1,4 @@
1
- import BTModule, { BTTile, IntArray2D, createOneTileResult, } from '../data.js';
1
+ import BTModule, { BTTile, IntArray2D, } from '../data.js';
2
2
  export default class DirectionLinkerBTModule extends BTModule {
3
3
  constructor(instr) {
4
4
  super();
@@ -8,31 +8,108 @@ export default class DirectionLinkerBTModule extends BTModule {
8
8
  writable: true,
9
9
  value: void 0
10
10
  });
11
+ Object.defineProperty(this, "initialPositions", {
12
+ enumerable: true,
13
+ configurable: true,
14
+ writable: true,
15
+ value: null
16
+ });
11
17
  this.instr = instr;
12
18
  }
19
+ getInitialPositions() {
20
+ if (this.instr.x % 1 !== 0 && this.instr.y % 1 !== 0)
21
+ return [
22
+ { x: Math.floor(this.instr.x), y: Math.floor(this.instr.y) },
23
+ { x: Math.ceil(this.instr.x), y: Math.ceil(this.instr.y) },
24
+ { x: Math.floor(this.instr.x), y: Math.ceil(this.instr.y) },
25
+ { x: Math.ceil(this.instr.x), y: Math.floor(this.instr.y) },
26
+ ];
27
+ else if (this.instr.x % 1 !== 0)
28
+ return [
29
+ { x: Math.floor(this.instr.x), y: this.instr.y },
30
+ { x: Math.ceil(this.instr.x), y: this.instr.y },
31
+ ];
32
+ else if (this.instr.y % 1 !== 0)
33
+ return [
34
+ { x: this.instr.x, y: Math.floor(this.instr.y) },
35
+ { x: this.instr.x, y: Math.ceil(this.instr.y) },
36
+ ];
37
+ else
38
+ return [{ x: this.instr.x, y: this.instr.y }];
39
+ }
13
40
  checkGlobal(grid) {
14
- const thisX = Math.floor(this.instr.x);
15
- const thisY = Math.floor(this.instr.y);
16
- const tile = grid.getTile(thisX, thisY);
17
- if (tile === BTTile.Empty)
18
- return createOneTileResult(grid, { x: thisX, y: thisY });
41
+ if (this.initialPositions === null)
42
+ this.initialPositions = this.getInitialPositions();
19
43
  const tilesNeedCheck = IntArray2D.create(grid.width, grid.height);
20
44
  const ratings = [];
21
- const queue = [{ x: thisX, y: thisY }];
45
+ let checkable = false;
46
+ for (const pos of this.initialPositions) {
47
+ const tile = grid.isInBound(pos.x, pos.y)
48
+ ? grid.getTile(pos.x, pos.y)
49
+ : BTTile.NonExist;
50
+ if (tile === BTTile.Empty) {
51
+ const oppoPos = this.movePos(grid, pos.x, pos.y);
52
+ if (oppoPos === null ||
53
+ grid.getTile(oppoPos.x, oppoPos.y) === BTTile.NonExist)
54
+ return false;
55
+ else {
56
+ tilesNeedCheck.set(pos.x, pos.y, 1);
57
+ ratings.push({ pos, score: 1 });
58
+ }
59
+ }
60
+ else if (tile === BTTile.NonExist) {
61
+ const oppoPos = this.movePos(grid, pos.x, pos.y);
62
+ if (oppoPos !== null &&
63
+ grid.getTile(oppoPos.x, oppoPos.y) !== BTTile.NonExist) {
64
+ return false;
65
+ }
66
+ }
67
+ else {
68
+ const oppoPos = this.movePos(grid, pos.x, pos.y);
69
+ if (oppoPos !== null) {
70
+ const oppoTile = grid.getTile(oppoPos.x, oppoPos.y);
71
+ if (oppoTile === BTTile.Empty) {
72
+ tilesNeedCheck.set(pos.x, pos.y, 1);
73
+ ratings.push({ pos, score: 1 });
74
+ }
75
+ else if (oppoTile === BTTile.NonExist)
76
+ return false;
77
+ else
78
+ checkable = true;
79
+ }
80
+ else {
81
+ return false;
82
+ }
83
+ }
84
+ }
85
+ if (!checkable) {
86
+ return { tilesNeedCheck, ratings };
87
+ }
88
+ const queue = this.initialPositions
89
+ .filter(pos => grid.isInBound(pos.x, pos.y) &&
90
+ grid.getTile(pos.x, pos.y) !== BTTile.NonExist)
91
+ .map(pos => {
92
+ const oppoPos = this.movePos(grid, pos.x, pos.y);
93
+ return {
94
+ pos,
95
+ color: grid.getTile(pos.x, pos.y),
96
+ oppoColor: grid.getTile(oppoPos.x, oppoPos.y),
97
+ };
98
+ });
22
99
  const visited = IntArray2D.create(grid.width, grid.height);
23
100
  // Visit all connected tiles
24
101
  while (queue.length > 0) {
25
102
  const curPos = queue.pop();
26
- if (visited.get(curPos.x, curPos.y))
103
+ if (visited.get(curPos.pos.x, curPos.pos.y))
27
104
  continue;
28
- visited.set(curPos.x, curPos.y, 1);
29
- const oppoPos = this.movePos(grid, curPos.x, curPos.y);
105
+ visited.set(curPos.pos.x, curPos.pos.y, 1);
106
+ const oppoPos = this.movePos(grid, curPos.pos.x, curPos.pos.y);
30
107
  if (oppoPos === null)
31
108
  return false;
32
109
  const oppoTile = grid.getTile(oppoPos.x, oppoPos.y);
33
- if (!(oppoTile === BTTile.Empty || oppoTile === tile))
110
+ if (!(oppoTile === BTTile.Empty || oppoTile === curPos.oppoColor))
34
111
  return false;
35
- for (const edge of grid.getEdges(curPos)) {
112
+ for (const edge of grid.getEdges(curPos.pos)) {
36
113
  if (visited.get(edge.x, edge.y))
37
114
  continue;
38
115
  const edgeTile = grid.getTile(edge.x, edge.y);
@@ -40,8 +117,12 @@ export default class DirectionLinkerBTModule extends BTModule {
40
117
  tilesNeedCheck.set(edge.x, edge.y, 1);
41
118
  ratings.push({ pos: edge, score: 1 });
42
119
  }
43
- else if (edgeTile === tile) {
44
- queue.push(edge);
120
+ else if (edgeTile === curPos.color) {
121
+ queue.push({
122
+ pos: edge,
123
+ color: curPos.color,
124
+ oppoColor: curPos.oppoColor,
125
+ });
45
126
  }
46
127
  }
47
128
  }
@@ -1,9 +1,11 @@
1
1
  import { Position } from '../../../primitives.js';
2
2
  import LotusSymbol from '../../../symbols/lotusSymbol.js';
3
- import { BTGridData } from '../data.js';
3
+ import { BTGridData, CheckResult } from '../data.js';
4
4
  import DirectionLinkerBTModule from './directionLinker.js';
5
5
  export default class LotusBTModule extends DirectionLinkerBTModule {
6
6
  instr: LotusSymbol;
7
7
  constructor(instr: LotusSymbol);
8
8
  protected movePos(grid: BTGridData, x: number, y: number): Position | null;
9
+ private getTileSafe;
10
+ checkGlobal(grid: BTGridData): false | CheckResult;
9
11
  }
@@ -1,4 +1,5 @@
1
1
  import { Orientation } from '../../../primitives.js';
2
+ import { BTTile } from '../data.js';
2
3
  import DirectionLinkerBTModule from './directionLinker.js';
3
4
  export default class LotusBTModule extends DirectionLinkerBTModule {
4
5
  constructor(instr) {
@@ -33,4 +34,27 @@ export default class LotusBTModule extends DirectionLinkerBTModule {
33
34
  }
34
35
  return grid.isInBound(pos.x, pos.y) ? pos : null;
35
36
  }
37
+ getTileSafe(grid, x, y) {
38
+ return grid.isInBound(x, y) ? grid.getTile(x, y) : BTTile.NonExist;
39
+ }
40
+ checkGlobal(grid) {
41
+ if (this.instr.orientation === Orientation.DownLeft ||
42
+ this.instr.orientation === Orientation.DownRight ||
43
+ this.instr.orientation === Orientation.UpLeft ||
44
+ this.instr.orientation === Orientation.UpRight) {
45
+ if (this.instr.x % 1 === 0 || this.instr.y % 1 === 0)
46
+ if (this.instr.x % 1 !== 0 || this.instr.y % 1 !== 0) {
47
+ if (this.getTileSafe(grid, Math.floor(this.instr.x), Math.floor(this.instr.y)) === BTTile.NonExist &&
48
+ this.getTileSafe(grid, Math.ceil(this.instr.x), Math.ceil(this.instr.y)) === BTTile.NonExist &&
49
+ this.getTileSafe(grid, Math.floor(this.instr.x), Math.ceil(this.instr.y)) === BTTile.NonExist &&
50
+ this.getTileSafe(grid, Math.ceil(this.instr.x), Math.floor(this.instr.y)) === BTTile.NonExist) {
51
+ return { tilesNeedCheck: null, ratings: null };
52
+ }
53
+ else {
54
+ return false;
55
+ }
56
+ }
57
+ }
58
+ return super.checkGlobal(grid);
59
+ }
36
60
  }
@@ -24,7 +24,6 @@ export default class DirectionLinkerSymbol extends Symbol {
24
24
  get explanation(): string;
25
25
  get configs(): readonly AnyConfig[] | null;
26
26
  createExampleGrid(): GridData;
27
- private getColor;
28
27
  private deltaCoordinate;
29
28
  validateSymbol(grid: GridData): State;
30
29
  copyWith({ x, y }: {
@@ -2,6 +2,20 @@ import { ConfigType } from '../config.js';
2
2
  import GridData from '../grid.js';
3
3
  import { Color, Direction, State } from '../primitives.js';
4
4
  import Symbol from './symbol.js';
5
+ function getColor(c, grid) {
6
+ if (!grid.isPositionValid(c.x, c.y) || !grid.getTile(c.x, c.y).exists) {
7
+ return null;
8
+ }
9
+ return grid.getTile(c.x, c.y).color;
10
+ }
11
+ function makeTurtle(pos1, pos2, grid) {
12
+ return {
13
+ pos1,
14
+ color1: getColor(pos1, grid),
15
+ pos2,
16
+ color2: getColor(pos2, grid),
17
+ };
18
+ }
5
19
  class DirectionLinkerSymbol extends Symbol {
6
20
  /**
7
21
  * **Darts count opposite color cells in that direction**
@@ -51,12 +65,6 @@ class DirectionLinkerSymbol extends Symbol {
51
65
  createExampleGrid() {
52
66
  return DirectionLinkerSymbol.EXAMPLE_GRID;
53
67
  }
54
- getColor(c, grid) {
55
- if (!grid.isPositionValid(c.x, c.y) || !grid.getTile(c.x, c.y).exists) {
56
- return null;
57
- }
58
- return grid.getTile(c.x, c.y).color;
59
- }
60
68
  deltaCoordinate(c, direction) {
61
69
  return {
62
70
  x: c.x + DirectionLinkerSymbol.directionDeltas[direction].dx,
@@ -73,49 +81,56 @@ class DirectionLinkerSymbol extends Symbol {
73
81
  // If the color of both are the opposite of the turtle, the turtle doesn't fire any other turtle
74
82
  // If no turtle remains, go to final state
75
83
  // Final state is State.Satisfied if no gray cell is found, State.Incomplete otherwise
76
- const baseColor = grid.getTile(Math.round(this.x), Math.round(this.y)).color;
77
- if (baseColor === Color.Gray) {
84
+ let checkedCouples = this.getInitialCheckedCouples(this.x, this.y, grid);
85
+ if (checkedCouples.some(({ color1, color2 }) => (color1 === null && color2 !== null) ||
86
+ (color1 !== null && color2 === null))) {
87
+ return State.Error;
88
+ }
89
+ if (checkedCouples.some(({ color1, color2 }) => color1 === Color.Gray || color2 === Color.Gray)) {
78
90
  return State.Incomplete;
79
91
  }
80
- const checkedCouples = this.getInitialCheckedCouples(this.x, this.y);
81
- const queue = checkedCouples.map(([p1, p2]) => [
82
- { ...p1 },
83
- { ...p2 },
84
- ]);
92
+ checkedCouples = checkedCouples.filter(({ color1, color2 }) => color1 !== null && color2 !== null);
93
+ const queue = checkedCouples.slice();
85
94
  let grayFound = false;
86
95
  while (queue.length > 0) {
87
96
  const turtle = queue.shift();
88
- const [c1, c2] = turtle;
89
- const color1 = this.getColor(c1, grid);
90
- const color2 = this.getColor(c2, grid);
91
- const colorSet = new Set([color1, color2]);
92
- if (colorSet.has(null) && !colorSet.has(baseColor)) {
93
- colorSet.delete(null);
97
+ const { pos1, pos2, color1: baseColor1, color2: baseColor2 } = turtle;
98
+ const color1 = getColor(pos1, grid);
99
+ const color2 = getColor(pos2, grid);
100
+ if (color1 === Color.Gray || color2 === Color.Gray) {
101
+ grayFound = true;
94
102
  }
95
- if (colorSet.size === 2 && !colorSet.has(Color.Gray)) {
96
- return State.Error;
103
+ else if (color1 === null || color2 === null) {
104
+ if ((color1 === null && color2 === baseColor2) ||
105
+ (color2 === null && color1 === baseColor1)) {
106
+ return State.Error;
107
+ }
97
108
  }
98
- if (colorSet.has(Color.Gray)) {
99
- grayFound = true;
109
+ else if (color1 !== baseColor1 || color2 !== baseColor2) {
110
+ if (color1 === baseColor1 || color2 === baseColor2) {
111
+ return State.Error;
112
+ }
100
113
  }
101
- if (color1 === baseColor) {
114
+ if (color1 === baseColor1) {
102
115
  const directions = Object.keys(this.linkedDirections);
103
116
  for (const direction of directions) {
104
- const newTurtle = [
105
- this.deltaCoordinate(c1, direction),
106
- this.deltaCoordinate(c2, this.linkedDirections[direction]),
107
- ];
108
- if (checkedCouples.some(([c1, c2]) => c1.x === newTurtle[0].x &&
109
- c1.y === newTurtle[0].y &&
110
- c2.x === newTurtle[1].x &&
111
- c2.y === newTurtle[1].y) ||
112
- (c1.x === newTurtle[1].x &&
113
- c1.y === newTurtle[1].y &&
114
- c2.x === newTurtle[0].x &&
115
- c2.y === newTurtle[0].y)) {
117
+ const newTurtle = {
118
+ pos1: this.deltaCoordinate(pos1, direction),
119
+ pos2: this.deltaCoordinate(pos2, this.linkedDirections[direction]),
120
+ color1: baseColor1,
121
+ color2: baseColor2,
122
+ };
123
+ if (checkedCouples.some(({ pos1, pos2 }) => pos1.x === newTurtle.pos1.x &&
124
+ pos1.y === newTurtle.pos1.y &&
125
+ pos2.x === newTurtle.pos2.x &&
126
+ pos2.y === newTurtle.pos2.y) ||
127
+ (pos1.x === newTurtle.pos2.x &&
128
+ pos1.y === newTurtle.pos2.y &&
129
+ pos2.x === newTurtle.pos1.x &&
130
+ pos2.y === newTurtle.pos1.y)) {
116
131
  continue;
117
132
  }
118
- checkedCouples.push([newTurtle[0], newTurtle[1]]);
133
+ checkedCouples.push(newTurtle);
119
134
  queue.push(newTurtle);
120
135
  }
121
136
  }
@@ -125,73 +140,50 @@ class DirectionLinkerSymbol extends Symbol {
125
140
  copyWith({ x, y }) {
126
141
  return new DirectionLinkerSymbol(x ?? this.x, y ?? this.y);
127
142
  }
128
- getInitialCheckedCouples(x, y) {
143
+ getInitialCheckedCouples(x, y, grid) {
129
144
  // 1x1
130
145
  if (x % 1 === 0 && y % 1 === 0) {
131
- return [
132
- [
133
- { x, y },
134
- { x, y },
135
- ],
136
- ];
146
+ return [makeTurtle({ x, y }, { x, y }, grid)];
137
147
  }
138
148
  // 1x2
139
149
  if (x % 1 === 0) {
140
150
  return [
141
- [
142
- { x, y: y - 0.5 },
143
- {
144
- x,
145
- y: y +
146
- (this.linkedDirections[Direction.Up] === Direction.Up &&
147
- this.linkedDirections[Direction.Down] === Direction.Down
148
- ? -0.5
149
- : 0.5),
150
- },
151
- ],
151
+ makeTurtle({ x, y: y - 0.5 }, {
152
+ x,
153
+ y: y +
154
+ (this.linkedDirections[Direction.Up] === Direction.Up &&
155
+ this.linkedDirections[Direction.Down] === Direction.Down
156
+ ? -0.5
157
+ : 0.5),
158
+ }, grid),
152
159
  ];
153
160
  }
154
161
  // 2x1
155
162
  if (y % 1 === 0) {
156
163
  return [
157
- [
158
- { x: x - 0.5, y },
159
- {
160
- x: x +
161
- (this.linkedDirections[Direction.Left] === Direction.Left &&
162
- this.linkedDirections[Direction.Right] === Direction.Right
163
- ? -0.5
164
- : 0.5),
165
- y,
166
- },
167
- ],
164
+ makeTurtle({ x: x - 0.5, y }, {
165
+ x: x +
166
+ (this.linkedDirections[Direction.Left] === Direction.Left &&
167
+ this.linkedDirections[Direction.Right] === Direction.Right
168
+ ? -0.5
169
+ : 0.5),
170
+ y,
171
+ }, grid),
168
172
  ];
169
173
  }
170
174
  // 2x2
171
175
  if (this.linkedDirections[Direction.Left] === Direction.Left &&
172
176
  this.linkedDirections[Direction.Right] === Direction.Right) {
173
177
  return [
174
- [
175
- { x: x - 0.5, y: y - 0.5 },
176
- { x: x - 0.5, y: y + 0.5 },
177
- ],
178
- [
179
- { x: x + 0.5, y: y - 0.5 },
180
- { x: x + 0.5, y: y + 0.5 },
181
- ],
178
+ makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x - 0.5, y: y + 0.5 }, grid),
179
+ makeTurtle({ x: x + 0.5, y: y - 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
182
180
  ];
183
181
  }
184
182
  if (this.linkedDirections[Direction.Up] === Direction.Up &&
185
183
  this.linkedDirections[Direction.Down] === Direction.Down) {
186
184
  return [
187
- [
188
- { x: x - 0.5, y: y - 0.5 },
189
- { x: x + 0.5, y: y - 0.5 },
190
- ],
191
- [
192
- { x: x - 0.5, y: y + 0.5 },
193
- { x: x + 0.5, y: y + 0.5 },
194
- ],
185
+ makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x + 0.5, y: y - 0.5 }, grid),
186
+ makeTurtle({ x: x - 0.5, y: y + 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
195
187
  ];
196
188
  }
197
189
  else if ((this.linkedDirections[Direction.Up] === Direction.Left &&
@@ -199,21 +191,12 @@ class DirectionLinkerSymbol extends Symbol {
199
191
  (this.linkedDirections[Direction.Up] === Direction.Right &&
200
192
  this.linkedDirections[Direction.Right] === Direction.Up)) {
201
193
  return [
202
- [
203
- { x: x - 0.5, y: y - 0.5 },
204
- { x: x - 0.5, y: y - 0.5 },
205
- ],
194
+ makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x - 0.5, y: y - 0.5 }, grid),
206
195
  ];
207
196
  }
208
197
  return [
209
- [
210
- { x: x - 0.5, y: y - 0.5 },
211
- { x: x + 0.5, y: y + 0.5 },
212
- ],
213
- [
214
- { x: x - 0.5, y: y + 0.5 },
215
- { x: x + 0.5, y: y - 0.5 },
216
- ],
198
+ makeTurtle({ x: x - 0.5, y: y - 0.5 }, { x: x + 0.5, y: y + 0.5 }, grid),
199
+ makeTurtle({ x: x - 0.5, y: y + 0.5 }, { x: x + 0.5, y: y - 0.5 }, grid),
217
200
  ];
218
201
  }
219
202
  }
@@ -7,6 +7,7 @@ export default class HiddenSymbol extends Symbol implements SymbolDisplayHandler
7
7
  readonly x: number;
8
8
  readonly y: number;
9
9
  readonly color: Color;
10
+ readonly revealLocation: boolean;
10
11
  private static readonly CONFIGS;
11
12
  private static readonly EXAMPLE_GRID;
12
13
  /**
@@ -15,8 +16,9 @@ export default class HiddenSymbol extends Symbol implements SymbolDisplayHandler
15
16
  * @param x - The x-coordinate of the symbol.
16
17
  * @param y - The y-coordinate of the symbol.
17
18
  * @param color - The target color of the cell.
19
+ * @param revealLocation - Whether to reveal the location of the symbol.
18
20
  */
19
- constructor(x: number, y: number, color: Color);
21
+ constructor(x: number, y: number, color: Color, revealLocation?: boolean);
20
22
  get id(): string;
21
23
  get explanation(): string;
22
24
  get configs(): readonly AnyConfig[] | null;
@@ -26,11 +28,13 @@ export default class HiddenSymbol extends Symbol implements SymbolDisplayHandler
26
28
  get sortOrder(): number;
27
29
  validateSymbol(grid: GridData): State;
28
30
  onSymbolDisplay(grid: GridData, symbol: Symbol, editing: boolean): boolean;
29
- copyWith({ x, y, color, }: {
31
+ copyWith({ x, y, color, revealLocation, }: {
30
32
  x?: number;
31
33
  y?: number;
32
34
  color?: Color;
35
+ revealLocation?: boolean;
33
36
  }): this;
34
37
  withColor(color: Color): this;
38
+ withRevealLocation(revealLocation: boolean): this;
35
39
  }
36
40
  export declare const instance: HiddenSymbol;
@@ -10,8 +10,9 @@ class HiddenSymbol extends Symbol {
10
10
  * @param x - The x-coordinate of the symbol.
11
11
  * @param y - The y-coordinate of the symbol.
12
12
  * @param color - The target color of the cell.
13
+ * @param revealLocation - Whether to reveal the location of the symbol.
13
14
  */
14
- constructor(x, y, color) {
15
+ constructor(x, y, color, revealLocation = false) {
15
16
  super(x, y);
16
17
  Object.defineProperty(this, "x", {
17
18
  enumerable: true,
@@ -31,7 +32,14 @@ class HiddenSymbol extends Symbol {
31
32
  writable: true,
32
33
  value: color
33
34
  });
35
+ Object.defineProperty(this, "revealLocation", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: revealLocation
40
+ });
34
41
  this.color = color;
42
+ this.revealLocation = revealLocation;
35
43
  }
36
44
  get id() {
37
45
  return `hidden`;
@@ -49,7 +57,7 @@ class HiddenSymbol extends Symbol {
49
57
  return false;
50
58
  }
51
59
  get visibleWhenSolving() {
52
- return false;
60
+ return this.revealLocation;
53
61
  }
54
62
  get sortOrder() {
55
63
  return 0;
@@ -62,7 +70,7 @@ class HiddenSymbol extends Symbol {
62
70
  : State.Incomplete;
63
71
  }
64
72
  onSymbolDisplay(grid, symbol, editing) {
65
- if (symbol.id === this.id || editing)
73
+ if (editing)
66
74
  return true;
67
75
  const thisX = Math.floor(this.x);
68
76
  const thisY = Math.floor(this.y);
@@ -70,14 +78,21 @@ class HiddenSymbol extends Symbol {
70
78
  const symY = Math.floor(symbol.y);
71
79
  if (thisX !== symX || thisY !== symY)
72
80
  return true;
73
- return grid.getTile(thisX, thisY).color === this.color;
81
+ const colorMatch = grid.getTile(thisX, thisY).color === this.color;
82
+ if (symbol.id === this.id) {
83
+ return !colorMatch;
84
+ }
85
+ return colorMatch;
74
86
  }
75
- copyWith({ x, y, color, }) {
76
- return new HiddenSymbol(x ?? this.x, y ?? this.y, color ?? this.color);
87
+ copyWith({ x, y, color, revealLocation, }) {
88
+ return new HiddenSymbol(x ?? this.x, y ?? this.y, color ?? this.color, revealLocation ?? this.revealLocation);
77
89
  }
78
90
  withColor(color) {
79
91
  return this.copyWith({ color });
80
92
  }
93
+ withRevealLocation(revealLocation) {
94
+ return this.copyWith({ revealLocation });
95
+ }
81
96
  }
82
97
  Object.defineProperty(HiddenSymbol, "CONFIGS", {
83
98
  enumerable: true,
@@ -106,6 +121,13 @@ Object.defineProperty(HiddenSymbol, "CONFIGS", {
106
121
  description: 'Show on color',
107
122
  configurable: true,
108
123
  },
124
+ {
125
+ type: ConfigType.Boolean,
126
+ default: false,
127
+ field: 'revealLocation',
128
+ description: 'Reveal symbol location',
129
+ configurable: true,
130
+ },
109
131
  ])
110
132
  });
111
133
  Object.defineProperty(HiddenSymbol, "EXAMPLE_GRID", {
@@ -1,6 +1,6 @@
1
1
  import { ConfigType } from '../config.js';
2
2
  import GridData from '../grid.js';
3
- import { Direction, Orientation } from '../primitives.js';
3
+ import { Direction, Orientation, State } from '../primitives.js';
4
4
  import DirectionLinkerSymbol from './directionLinkerSymbol.js';
5
5
  class LotusSymbol extends DirectionLinkerSymbol {
6
6
  /**
@@ -67,6 +67,23 @@ class LotusSymbol extends DirectionLinkerSymbol {
67
67
  return Object.freeze(GridData.create(['wwbww', 'bwbwb', 'bwwwb', 'bwwwb']).addSymbol(new LotusSymbol(2, 2, Orientation.Up)));
68
68
  }
69
69
  validateSymbol(grid) {
70
+ if (this.orientation === Orientation.DownLeft ||
71
+ this.orientation === Orientation.DownRight ||
72
+ this.orientation === Orientation.UpLeft ||
73
+ this.orientation === Orientation.UpRight) {
74
+ if (this.x % 1 === 0 || this.y % 1 === 0)
75
+ if (this.x % 1 !== 0 || this.y % 1 !== 0) {
76
+ if (!grid.getTile(Math.floor(this.x), Math.floor(this.y)).exists &&
77
+ !grid.getTile(Math.ceil(this.x), Math.ceil(this.y)).exists &&
78
+ !grid.getTile(Math.floor(this.x), Math.ceil(this.y)).exists &&
79
+ !grid.getTile(Math.ceil(this.x), Math.floor(this.y)).exists) {
80
+ return State.Satisfied;
81
+ }
82
+ else {
83
+ return State.Error;
84
+ }
85
+ }
86
+ }
70
87
  return super.validateSymbol(grid);
71
88
  }
72
89
  copyWith({ x, y, orientation, }) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logic-pad/core",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist",