@logic-pad/core 0.26.3 → 0.27.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.
- package/assets/logic-core.global.d.ts +1 -2
- package/dist/benchmark/helper.d.ts +1 -0
- package/dist/benchmark/helper.js +12 -0
- package/dist/benchmark/prepareBench.js +34 -20
- package/dist/benchmark/runBench.js +28 -10
- package/dist/src/data/rules/symbolsPerRegionRule.d.ts +0 -1
- package/dist/src/data/rules/symbolsPerRegionRule.js +27 -14
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.d.ts +1 -1
- package/dist/src/data/solver/backtrack/rules/symbolsPerRegion.js +32 -12
- package/package.json +3 -1
|
@@ -2135,7 +2135,6 @@ declare global {
|
|
|
2135
2135
|
withColor(color: Color): this;
|
|
2136
2136
|
withCount(count: number): this;
|
|
2137
2137
|
withComparison(comparison: Comparison): this;
|
|
2138
|
-
private static countAllSymbolsOfPosition;
|
|
2139
2138
|
}
|
|
2140
2139
|
export declare class UniqueShapeRule extends RegionShapeRule {
|
|
2141
2140
|
readonly title = 'Unique Shape Areas';
|
|
@@ -2538,7 +2537,7 @@ declare global {
|
|
|
2538
2537
|
}
|
|
2539
2538
|
export declare class SymbolsPerRegionBTModule extends BTModule {
|
|
2540
2539
|
instr: SymbolsPerRegionRule;
|
|
2541
|
-
private
|
|
2540
|
+
private symbolMap;
|
|
2542
2541
|
constructor(
|
|
2543
2542
|
instr: SymbolsPerRegionRule,
|
|
2544
2543
|
width: number,
|
package/dist/benchmark/helper.js
CHANGED
|
@@ -32,3 +32,15 @@ export async function parseLink(link) {
|
|
|
32
32
|
}
|
|
33
33
|
return Serializer.parsePuzzle(await bunCompressor.decompress(data));
|
|
34
34
|
}
|
|
35
|
+
export function shuffleArray(array) {
|
|
36
|
+
let currentIndex = array.length;
|
|
37
|
+
while (currentIndex !== 0) {
|
|
38
|
+
const randomIndex = Math.floor(Math.random() * currentIndex);
|
|
39
|
+
currentIndex--;
|
|
40
|
+
[array[currentIndex], array[randomIndex]] = [
|
|
41
|
+
array[randomIndex],
|
|
42
|
+
array[currentIndex],
|
|
43
|
+
];
|
|
44
|
+
}
|
|
45
|
+
return array;
|
|
46
|
+
}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
import { parseArgs } from 'util';
|
|
2
2
|
import { allSolvers } from '../src/data/solver/allSolvers.js';
|
|
3
|
-
import { parseLink } from './helper.js';
|
|
3
|
+
import { parseLink, shuffleArray, } from './helper.js';
|
|
4
4
|
import PQueue from 'p-queue';
|
|
5
|
-
const allPuzzlesPath = 'benchmark/data/all_puzzles_logic_pad.json';
|
|
6
5
|
const { values, positionals } = parseArgs({
|
|
7
6
|
args: Bun.argv,
|
|
8
7
|
options: {
|
|
8
|
+
name: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
short: 'n',
|
|
11
|
+
},
|
|
12
|
+
file: {
|
|
13
|
+
type: 'string',
|
|
14
|
+
short: 'f',
|
|
15
|
+
},
|
|
9
16
|
maxTime: {
|
|
10
17
|
type: 'string',
|
|
11
18
|
default: '10',
|
|
@@ -13,13 +20,13 @@ const { values, positionals } = parseArgs({
|
|
|
13
20
|
},
|
|
14
21
|
maxCount: {
|
|
15
22
|
type: 'string',
|
|
16
|
-
default: '
|
|
23
|
+
default: '200',
|
|
17
24
|
short: 'c',
|
|
18
25
|
},
|
|
19
26
|
concurrency: {
|
|
20
27
|
type: 'string',
|
|
21
28
|
default: '4',
|
|
22
|
-
short: '
|
|
29
|
+
short: 'd',
|
|
23
30
|
},
|
|
24
31
|
help: {
|
|
25
32
|
type: 'boolean',
|
|
@@ -30,14 +37,17 @@ const { values, positionals } = parseArgs({
|
|
|
30
37
|
allowPositionals: true,
|
|
31
38
|
});
|
|
32
39
|
positionals.splice(0, 2); // Remove "bun" and script name
|
|
33
|
-
if (values.help || positionals.length !== 1) {
|
|
40
|
+
if (values.help || positionals.length !== 1 || !values.file) {
|
|
34
41
|
console.log(`
|
|
35
42
|
Usage: bun bench:prepare <solver> [options]
|
|
36
43
|
|
|
37
44
|
Options:
|
|
45
|
+
-f, --file <string> Path to the puzzle data file (required)
|
|
46
|
+
|
|
47
|
+
-n, --name <string> Name of the generated benchmark files (default: solver name)
|
|
38
48
|
-t, --maxTime <number> Maximum seconds allowed for each solve (default: 10)
|
|
39
49
|
-c, --maxCount <number> Maximum number of puzzles included (default: 100)
|
|
40
|
-
-
|
|
50
|
+
-d, --concurrency <number> Number of solves to run concurrently (default: 4)
|
|
41
51
|
-h, --help Show this help message
|
|
42
52
|
|
|
43
53
|
Solver:
|
|
@@ -49,31 +59,36 @@ ${[...allSolvers.keys()].map(s => ` - ${s}`).join('\n')}
|
|
|
49
59
|
const maxTime = parseFloat(values.maxTime) * 1000;
|
|
50
60
|
const maxCount = parseInt(values.maxCount);
|
|
51
61
|
const solverName = positionals[0];
|
|
62
|
+
const outputName = values.name ?? solverName;
|
|
63
|
+
const allPuzzlesPath = `benchmark/data/${values.file}`;
|
|
52
64
|
const solver = allSolvers.get(solverName);
|
|
53
65
|
if (!solver) {
|
|
54
66
|
console.error(`Error: Solver "${solverName}" not found.`);
|
|
55
67
|
process.exit(1);
|
|
56
68
|
}
|
|
57
69
|
const allPuzzles = (await Bun.file(allPuzzlesPath).json());
|
|
58
|
-
allPuzzles
|
|
70
|
+
shuffleArray(allPuzzles);
|
|
59
71
|
const results = [];
|
|
60
72
|
const pqueue = new PQueue({ concurrency: 4 });
|
|
61
|
-
|
|
73
|
+
pqueue.on('completed', () => {
|
|
74
|
+
if (results.filter(r => r.result.solveCorrect && r.result.supported).length >=
|
|
75
|
+
maxCount) {
|
|
76
|
+
pqueue.clear();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
function printEntry(benchmarkEntry, entryId, pid) {
|
|
80
|
+
const selectedPuzzles = results.filter(r => r.result.solveCorrect && r.result.supported);
|
|
62
81
|
if (benchmarkEntry.supported) {
|
|
63
|
-
console.log(`${
|
|
82
|
+
console.log(`${entryId} / ${allPuzzles.length} \t| ${selectedPuzzles.length} / ${maxCount} \t| ${pid}\t| ${Number.isNaN(benchmarkEntry.solveTime)
|
|
64
83
|
? 'timeout'
|
|
65
84
|
: `${benchmarkEntry.solveTime.toFixed(0)}ms`} ${benchmarkEntry.solveCorrect ? '✓' : '✗'}`);
|
|
66
85
|
}
|
|
67
86
|
else {
|
|
68
|
-
console.log(`${
|
|
87
|
+
console.log(`${entryId} / ${allPuzzles.length} \t| ${selectedPuzzles.length} / ${maxCount} \t| ${pid}\t| unsupported`);
|
|
69
88
|
}
|
|
70
89
|
}
|
|
71
90
|
console.log('Available\t| Selected\t| PID\t| Result');
|
|
72
91
|
for (const entry of allPuzzles) {
|
|
73
|
-
if (results.filter(r => r.result.solveCorrect && r.result.supported).length >=
|
|
74
|
-
maxCount) {
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
92
|
const puzzle = await parseLink(entry.puzzleLink);
|
|
78
93
|
void pqueue.add(async () => {
|
|
79
94
|
if (!solver.isGridSupported(puzzle.grid)) {
|
|
@@ -86,7 +101,7 @@ for (const entry of allPuzzles) {
|
|
|
86
101
|
solveCorrect: false,
|
|
87
102
|
},
|
|
88
103
|
});
|
|
89
|
-
printEntry(results[results.length - 1].result, results.length,
|
|
104
|
+
printEntry(results[results.length - 1].result, results.length, entry.pid);
|
|
90
105
|
return;
|
|
91
106
|
}
|
|
92
107
|
const startTime = performance.now();
|
|
@@ -122,19 +137,18 @@ for (const entry of allPuzzles) {
|
|
|
122
137
|
}
|
|
123
138
|
clearTimeout(handle);
|
|
124
139
|
results.push({ puzzle: entry, result: benchmarkEntry });
|
|
125
|
-
printEntry(results[results.length - 1].result, results.length,
|
|
140
|
+
printEntry(results[results.length - 1].result, results.length, entry.pid);
|
|
126
141
|
});
|
|
127
142
|
}
|
|
128
143
|
await pqueue.onIdle();
|
|
129
144
|
const selectedPuzzles = results.filter(r => r.result.solveCorrect && r.result.supported);
|
|
130
145
|
const benchmarkEntries = selectedPuzzles.map(r => r.result);
|
|
131
|
-
await Bun.write(`benchmark/data
|
|
132
|
-
await Bun.write(`benchmark/data
|
|
146
|
+
await Bun.write(`benchmark/data/${outputName}_bench_puzzles.json`, JSON.stringify(selectedPuzzles.map(r => r.puzzle), null, 2));
|
|
147
|
+
await Bun.write(`benchmark/data/${outputName}_bench_results.json`, JSON.stringify(benchmarkEntries, null, 2));
|
|
133
148
|
console.log(`
|
|
134
149
|
Benchmark preparation completed. Selected ${selectedPuzzles.length} puzzles.
|
|
135
150
|
|
|
136
151
|
- Solver: ${solverName} Max Time: ${maxTime}ms Max Count: ${maxCount}
|
|
137
|
-
- Average time: ${
|
|
138
|
-
- Number of timeouts: ${benchmarkEntries.filter(e => Number.isNaN(e.solveTime)).length}
|
|
152
|
+
- Average time: ${selectedPuzzles.reduce((a, b) => a + b.result.solveTime, 0) / selectedPuzzles.length}ms
|
|
139
153
|
- Solve correctness: ${benchmarkEntries.filter(e => e.solveCorrect).length} / ${benchmarkEntries.length}
|
|
140
154
|
`);
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import { parseArgs } from 'util';
|
|
2
2
|
import { allSolvers } from '../src/data/solver/allSolvers.js';
|
|
3
|
-
import { parseLink } from './helper.js';
|
|
3
|
+
import { parseLink, shuffleArray, } from './helper.js';
|
|
4
4
|
import PQueue from 'p-queue';
|
|
5
5
|
const { values, positionals } = parseArgs({
|
|
6
6
|
args: Bun.argv,
|
|
7
7
|
options: {
|
|
8
|
+
name: {
|
|
9
|
+
type: 'string',
|
|
10
|
+
short: 'n',
|
|
11
|
+
},
|
|
8
12
|
maxTime: {
|
|
9
13
|
type: 'string',
|
|
10
14
|
default: '10',
|
|
@@ -12,13 +16,13 @@ const { values, positionals } = parseArgs({
|
|
|
12
16
|
},
|
|
13
17
|
maxCount: {
|
|
14
18
|
type: 'string',
|
|
15
|
-
default: '
|
|
19
|
+
default: '200',
|
|
16
20
|
short: 'c',
|
|
17
21
|
},
|
|
18
22
|
concurrency: {
|
|
19
23
|
type: 'string',
|
|
20
24
|
default: '4',
|
|
21
|
-
short: '
|
|
25
|
+
short: 'd',
|
|
22
26
|
},
|
|
23
27
|
help: {
|
|
24
28
|
type: 'boolean',
|
|
@@ -34,10 +38,11 @@ if (values.help || positionals.length === 0) {
|
|
|
34
38
|
Usage: bun bench:run <solver> [options]
|
|
35
39
|
|
|
36
40
|
Options:
|
|
37
|
-
-
|
|
38
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
+
-n, --name <string> Name of the generated benchmark files (default: first solver name)
|
|
42
|
+
-t, --maxTime <number> Maximum seconds allowed for each solve (default: 10)
|
|
43
|
+
-c, --maxCount <number> Maximum number of puzzles included (default: 100)
|
|
44
|
+
-n, --concurrency <number> Number of solves to run concurrently (default: 4)
|
|
45
|
+
-h, --help Show this help message
|
|
41
46
|
|
|
42
47
|
Solvers available for benchmarking:
|
|
43
48
|
${[...allSolvers.keys()].map(s => ` - ${s}`).join('\n')}
|
|
@@ -53,8 +58,9 @@ for (const name of positionals) {
|
|
|
53
58
|
process.exit(1);
|
|
54
59
|
}
|
|
55
60
|
}
|
|
56
|
-
const
|
|
57
|
-
allPuzzles
|
|
61
|
+
const outputName = values.name ?? positionals[0];
|
|
62
|
+
const allPuzzles = (await Bun.file(`benchmark/data/${outputName}_bench_puzzles.json`).json());
|
|
63
|
+
shuffleArray(allPuzzles);
|
|
58
64
|
allPuzzles.splice(maxCount);
|
|
59
65
|
const benchmarkEntries = Object.fromEntries(positionals.map(name => [
|
|
60
66
|
name,
|
|
@@ -139,9 +145,11 @@ const results = positionals.map(name => ({
|
|
|
139
145
|
solve25: 0,
|
|
140
146
|
solve50: 0,
|
|
141
147
|
solve75: 0,
|
|
148
|
+
solve90: 0,
|
|
142
149
|
solveSD: 0,
|
|
143
150
|
unsupportedCount: 0,
|
|
144
151
|
incorrectCount: 0,
|
|
152
|
+
timeoutCount: 0,
|
|
145
153
|
}));
|
|
146
154
|
for (let i = 0; i < benchmarkEntries[positionals[0]].length; i++) {
|
|
147
155
|
let fastestSolveTime = Number.POSITIVE_INFINITY;
|
|
@@ -158,9 +166,12 @@ for (let i = 0; i < benchmarkEntries[positionals[0]].length; i++) {
|
|
|
158
166
|
fastestSolverIndex = j;
|
|
159
167
|
}
|
|
160
168
|
}
|
|
161
|
-
else {
|
|
169
|
+
else if (!Number.isNaN(entry.solveTime)) {
|
|
162
170
|
results[j].incorrectCount++;
|
|
163
171
|
}
|
|
172
|
+
else {
|
|
173
|
+
results[j].timeoutCount++;
|
|
174
|
+
}
|
|
164
175
|
}
|
|
165
176
|
if (fastestSolverIndex !== null) {
|
|
166
177
|
results[fastestSolverIndex].fastestCount++;
|
|
@@ -184,6 +195,11 @@ for (let j = 0; j < positionals.length; j++) {
|
|
|
184
195
|
times.length === 0
|
|
185
196
|
? Number.NaN
|
|
186
197
|
: times[Math.floor((times.length - 1) * 0.75)];
|
|
198
|
+
// 90th percentile
|
|
199
|
+
results[j].solve90 =
|
|
200
|
+
times.length === 0
|
|
201
|
+
? Number.NaN
|
|
202
|
+
: times[Math.floor((times.length - 1) * 0.9)];
|
|
187
203
|
// Standard deviation
|
|
188
204
|
const mean = times.reduce((sum, time) => sum + time, 0) / (times.length || 1);
|
|
189
205
|
const variance = times.reduce((sum, time) => sum + (time - mean) ** 2, 0) /
|
|
@@ -199,8 +215,10 @@ ${result.solver}:
|
|
|
199
215
|
P25: ${Number.isNaN(result.solve25) ? 'N/A' : `${result.solve25.toFixed(2)}ms`}
|
|
200
216
|
P50: ${Number.isNaN(result.solve50) ? 'N/A' : `${result.solve50.toFixed(2)}ms`}
|
|
201
217
|
P75: ${Number.isNaN(result.solve75) ? 'N/A' : `${result.solve75.toFixed(2)}ms`}
|
|
218
|
+
P90: ${Number.isNaN(result.solve90) ? 'N/A' : `${result.solve90.toFixed(2)}ms`}
|
|
202
219
|
SD: ${Number.isNaN(result.solveSD) ? 'N/A' : `${result.solveSD.toFixed(2)}ms`}
|
|
203
220
|
Unsupported Puzzles: ${result.unsupportedCount}
|
|
204
221
|
Incorrect Solutions: ${result.incorrectCount}
|
|
222
|
+
Timeouts: ${result.timeoutCount}
|
|
205
223
|
`);
|
|
206
224
|
}
|
|
@@ -33,6 +33,5 @@ export default class SymbolsPerRegionRule extends Rule {
|
|
|
33
33
|
withColor(color: Color): this;
|
|
34
34
|
withCount(count: number): this;
|
|
35
35
|
withComparison(comparison: Comparison): this;
|
|
36
|
-
private static countAllSymbolsOfPosition;
|
|
37
36
|
}
|
|
38
37
|
export declare const instance: SymbolsPerRegionRule;
|
|
@@ -125,12 +125,24 @@ export default class SymbolsPerRegionRule extends Rule {
|
|
|
125
125
|
break;
|
|
126
126
|
const completed = [];
|
|
127
127
|
const gray = [];
|
|
128
|
+
const map = array(grid.width, grid.height, () => false);
|
|
128
129
|
let nbSymbolsIn = 0;
|
|
129
130
|
grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === this.color, (_, x, y) => {
|
|
130
131
|
completed.push({ x, y });
|
|
131
132
|
visited[y][x] = true;
|
|
132
|
-
|
|
133
|
-
|
|
133
|
+
}, map);
|
|
134
|
+
for (const [_, symbols] of grid.symbols) {
|
|
135
|
+
for (const symbol of symbols) {
|
|
136
|
+
if (!symbol.necessaryForCompletion)
|
|
137
|
+
continue;
|
|
138
|
+
if (map[Math.floor(symbol.y)][Math.floor(symbol.x)] ||
|
|
139
|
+
map[Math.floor(symbol.y)][Math.ceil(symbol.x)] ||
|
|
140
|
+
map[Math.ceil(symbol.y)][Math.floor(symbol.x)] ||
|
|
141
|
+
map[Math.ceil(symbol.y)][Math.ceil(symbol.x)]) {
|
|
142
|
+
nbSymbolsIn++;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
134
146
|
if (this.comparison !== Comparison.AtLeast && nbSymbolsIn > this.count) {
|
|
135
147
|
return { state: State.Error, positions: completed };
|
|
136
148
|
}
|
|
@@ -142,8 +154,20 @@ export default class SymbolsPerRegionRule extends Rule {
|
|
|
142
154
|
else {
|
|
143
155
|
grid.iterateArea({ x: seed.x, y: seed.y }, tile => tile.color === Color.Gray || tile.color === this.color, (_, x, y) => {
|
|
144
156
|
gray.push({ x, y });
|
|
145
|
-
|
|
157
|
+
map[y][x] = true;
|
|
146
158
|
});
|
|
159
|
+
for (const [_, symbols] of grid.symbols) {
|
|
160
|
+
for (const symbol of symbols) {
|
|
161
|
+
if (!symbol.necessaryForCompletion)
|
|
162
|
+
continue;
|
|
163
|
+
if (map[Math.floor(symbol.y)][Math.floor(symbol.x)] ||
|
|
164
|
+
map[Math.floor(symbol.y)][Math.ceil(symbol.x)] ||
|
|
165
|
+
map[Math.ceil(symbol.y)][Math.floor(symbol.x)] ||
|
|
166
|
+
map[Math.ceil(symbol.y)][Math.ceil(symbol.x)]) {
|
|
167
|
+
nbSymbolsOut++;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
147
171
|
}
|
|
148
172
|
if (this.comparison !== Comparison.AtMost && nbSymbolsOut < this.count) {
|
|
149
173
|
return { state: State.Error, positions: gray };
|
|
@@ -166,16 +190,5 @@ export default class SymbolsPerRegionRule extends Rule {
|
|
|
166
190
|
withComparison(comparison) {
|
|
167
191
|
return this.copyWith({ comparison });
|
|
168
192
|
}
|
|
169
|
-
static countAllSymbolsOfPosition(grid, x, y) {
|
|
170
|
-
let count = 0;
|
|
171
|
-
for (const symbolKind of grid.symbols.values()) {
|
|
172
|
-
if (symbolKind.some(symbol => Math.floor(symbol.x) === x &&
|
|
173
|
-
Math.floor(symbol.y) === y &&
|
|
174
|
-
symbol.necessaryForCompletion)) {
|
|
175
|
-
count++;
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
return count;
|
|
179
|
-
}
|
|
180
193
|
}
|
|
181
194
|
export const instance = new SymbolsPerRegionRule(Color.Dark, 1);
|
|
@@ -3,7 +3,7 @@ import Symbol from '../../../symbols/symbol.js';
|
|
|
3
3
|
import BTModule, { BTGridData, CheckResult } from '../data.js';
|
|
4
4
|
export default class SymbolsPerRegionBTModule extends BTModule {
|
|
5
5
|
instr: SymbolsPerRegionRule;
|
|
6
|
-
private
|
|
6
|
+
private symbolMap;
|
|
7
7
|
constructor(instr: SymbolsPerRegionRule, width: number, height: number, allSymbols: Symbol[]);
|
|
8
8
|
checkGlobal(grid: BTGridData): CheckResult | false;
|
|
9
9
|
private visitArea;
|
|
@@ -1,16 +1,33 @@
|
|
|
1
|
+
import { array } from '../../../dataHelper.js';
|
|
1
2
|
import { Comparison } from '../../../primitives.js';
|
|
2
3
|
import BTModule, { BTTile, IntArray2D, colorToBTTile, } from '../data.js';
|
|
3
4
|
export default class SymbolsPerRegionBTModule extends BTModule {
|
|
4
5
|
instr;
|
|
5
|
-
|
|
6
|
+
symbolMap = [];
|
|
6
7
|
constructor(instr, width, height, allSymbols) {
|
|
7
8
|
super();
|
|
8
9
|
this.instr = instr;
|
|
9
|
-
this.
|
|
10
|
+
this.symbolMap = array(width, height, () => []);
|
|
10
11
|
for (const symbol of allSymbols) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
if (Math.floor(symbol.x) >= 0 && Math.floor(symbol.y) >= 0) {
|
|
13
|
+
this.symbolMap[Math.floor(symbol.y)][Math.floor(symbol.x)].push(symbol);
|
|
14
|
+
}
|
|
15
|
+
if (Math.ceil(symbol.x) !== Math.floor(symbol.x) &&
|
|
16
|
+
Math.ceil(symbol.x) < width &&
|
|
17
|
+
Math.floor(symbol.y) >= 0) {
|
|
18
|
+
this.symbolMap[Math.floor(symbol.y)][Math.ceil(symbol.x)].push(symbol);
|
|
19
|
+
}
|
|
20
|
+
if (Math.ceil(symbol.y) !== Math.floor(symbol.y) &&
|
|
21
|
+
Math.floor(symbol.x) >= 0 &&
|
|
22
|
+
Math.ceil(symbol.y) < height) {
|
|
23
|
+
this.symbolMap[Math.ceil(symbol.y)][Math.floor(symbol.x)].push(symbol);
|
|
24
|
+
}
|
|
25
|
+
if (Math.ceil(symbol.x) !== Math.floor(symbol.x) &&
|
|
26
|
+
Math.ceil(symbol.y) !== Math.floor(symbol.y) &&
|
|
27
|
+
Math.ceil(symbol.x) < width &&
|
|
28
|
+
Math.ceil(symbol.y) < height) {
|
|
29
|
+
this.symbolMap[Math.ceil(symbol.y)][Math.ceil(symbol.x)].push(symbol);
|
|
30
|
+
}
|
|
14
31
|
}
|
|
15
32
|
}
|
|
16
33
|
checkGlobal(grid) {
|
|
@@ -35,13 +52,13 @@ export default class SymbolsPerRegionBTModule extends BTModule {
|
|
|
35
52
|
visitArea(grid, tile, visited, pos, id) {
|
|
36
53
|
const sameTileQueue = [pos];
|
|
37
54
|
const usableTileQueue = [];
|
|
38
|
-
|
|
39
|
-
|
|
55
|
+
const completed = new Set();
|
|
56
|
+
const possible = new Set();
|
|
40
57
|
visited.set(pos.x, pos.y, id);
|
|
41
58
|
// Count same tile
|
|
42
59
|
while (sameTileQueue.length > 0) {
|
|
43
60
|
const curPos = sameTileQueue.pop();
|
|
44
|
-
|
|
61
|
+
this.symbolMap[curPos.y][curPos.x].forEach(symbol => completed.add(symbol));
|
|
45
62
|
for (const edge of grid.getEdges(curPos)) {
|
|
46
63
|
if ((visited.get(edge.x, edge.y) & 0b01111111) === id)
|
|
47
64
|
continue;
|
|
@@ -56,7 +73,7 @@ export default class SymbolsPerRegionBTModule extends BTModule {
|
|
|
56
73
|
}
|
|
57
74
|
}
|
|
58
75
|
}
|
|
59
|
-
if (completed > this.instr.count) {
|
|
76
|
+
if (completed.size > this.instr.count) {
|
|
60
77
|
return this.instr.comparison === Comparison.AtLeast;
|
|
61
78
|
}
|
|
62
79
|
if (this.instr.comparison === Comparison.AtMost)
|
|
@@ -64,8 +81,11 @@ export default class SymbolsPerRegionBTModule extends BTModule {
|
|
|
64
81
|
// Count usable tile
|
|
65
82
|
while (usableTileQueue.length > 0) {
|
|
66
83
|
const curPos = usableTileQueue.pop();
|
|
67
|
-
|
|
68
|
-
|
|
84
|
+
this.symbolMap[curPos.y][curPos.x].forEach(symbol => {
|
|
85
|
+
if (!completed.has(symbol))
|
|
86
|
+
possible.add(symbol);
|
|
87
|
+
});
|
|
88
|
+
if (completed.size + possible.size >= this.instr.count)
|
|
69
89
|
return true;
|
|
70
90
|
for (const edge of grid.getEdges(curPos)) {
|
|
71
91
|
if ((visited.get(edge.x, edge.y) & 0b01111111) === id)
|
|
@@ -77,6 +97,6 @@ export default class SymbolsPerRegionBTModule extends BTModule {
|
|
|
77
97
|
}
|
|
78
98
|
}
|
|
79
99
|
}
|
|
80
|
-
return completed + possible >= this.instr.count;
|
|
100
|
+
return completed.size + possible.size >= this.instr.count;
|
|
81
101
|
}
|
|
82
102
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logic-pad/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
"typegen": "bun ./scripts/genTypes.ts",
|
|
49
49
|
"bench:prepare": "bun ./benchmark/prepareBench.ts",
|
|
50
50
|
"bench:run": "bun ./benchmark/runBench.ts",
|
|
51
|
+
"bench:prepare:lp": "bun bench:prepare universal -f all_puzzles_logic_pad.json -n lp",
|
|
52
|
+
"bench:run:lp": "bun bench:run universal universal_dev -n lp",
|
|
51
53
|
"prepublishOnly": "bun run build"
|
|
52
54
|
},
|
|
53
55
|
"engines": {
|