@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.
- package/assets/logic-core.global.d.ts +19 -3
- package/dist/data/solver/backtrack/backtrackSolver.js +1 -1
- package/dist/data/solver/backtrack/symbols/directionLinker.d.ts +2 -0
- package/dist/data/solver/backtrack/symbols/directionLinker.js +95 -14
- package/dist/data/solver/backtrack/symbols/lotus.d.ts +3 -1
- package/dist/data/solver/backtrack/symbols/lotus.js +24 -0
- package/dist/data/symbols/directionLinkerSymbol.d.ts +0 -1
- package/dist/data/symbols/directionLinkerSymbol.js +77 -94
- package/dist/data/symbols/hiddenSymbol.d.ts +6 -2
- package/dist/data/symbols/hiddenSymbol.js +28 -6
- package/dist/data/symbols/lotusSymbol.js +18 -1
- package/package.json +1 -1
|
@@ -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({
|
|
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(
|
|
@@ -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,
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
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 ===
|
|
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 ===
|
|
44
|
-
queue.push(
|
|
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
|
-
|
|
77
|
-
if (
|
|
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
|
-
|
|
81
|
-
const queue = checkedCouples.
|
|
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
|
|
89
|
-
const color1 =
|
|
90
|
-
const color2 =
|
|
91
|
-
|
|
92
|
-
|
|
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 (
|
|
96
|
-
|
|
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 (
|
|
99
|
-
|
|
109
|
+
else if (color1 !== baseColor1 || color2 !== baseColor2) {
|
|
110
|
+
if (color1 === baseColor1 || color2 === baseColor2) {
|
|
111
|
+
return State.Error;
|
|
112
|
+
}
|
|
100
113
|
}
|
|
101
|
-
if (color1 ===
|
|
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(
|
|
106
|
-
this.deltaCoordinate(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
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(
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
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
|
-
|
|
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, }) {
|