@hayro_o7/labyrinth 0.0.8 → 0.0.9
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.
|
@@ -1,6 +1,12 @@
|
|
|
1
|
-
import type { GeneralGraph, BFSResult } from '../types';
|
|
1
|
+
import type { GeneralGraph, BFSResult, BFSStep } from '../types';
|
|
2
2
|
export declare function bidirectionalBFS(graph: GeneralGraph, startId: string, endId: string): BFSResult;
|
|
3
3
|
export declare function findOptimalMultiGoalPath(graph: GeneralGraph, startId: string, goalIds: string[]): {
|
|
4
4
|
optimalPath: string[];
|
|
5
5
|
allPaths: Map<string, Map<string, string[]>>;
|
|
6
|
+
permutationSteps: BFSStep[];
|
|
7
|
+
testedPermutations: Array<{
|
|
8
|
+
path: string[];
|
|
9
|
+
length: number;
|
|
10
|
+
tour: string[];
|
|
11
|
+
}>;
|
|
6
12
|
};
|
|
@@ -109,6 +109,8 @@ function permutations(arr) {
|
|
|
109
109
|
}
|
|
110
110
|
export function findOptimalMultiGoalPath(graph, startId, goalIds) {
|
|
111
111
|
const allPaths = new Map();
|
|
112
|
+
const permutationSteps = [];
|
|
113
|
+
const testedPermutations = [];
|
|
112
114
|
// Compute paths between all pairs
|
|
113
115
|
const allNodes = [startId, ...goalIds];
|
|
114
116
|
for (const from of allNodes) {
|
|
@@ -126,8 +128,10 @@ export function findOptimalMultiGoalPath(graph, startId, goalIds) {
|
|
|
126
128
|
// Try all permutations to find shortest tour
|
|
127
129
|
let shortestPath = [];
|
|
128
130
|
let shortestLength = Infinity;
|
|
131
|
+
let bestPermIndex = -1;
|
|
129
132
|
const goalPermutations = permutations(goalIds);
|
|
130
|
-
for (
|
|
133
|
+
for (let permIndex = 0; permIndex < goalPermutations.length; permIndex++) {
|
|
134
|
+
const perm = goalPermutations[permIndex];
|
|
131
135
|
const tour = [startId, ...perm];
|
|
132
136
|
let totalPath = [];
|
|
133
137
|
let valid = true;
|
|
@@ -144,10 +148,38 @@ export function findOptimalMultiGoalPath(graph, startId, goalIds) {
|
|
|
144
148
|
totalPath = [...totalPath.slice(0, -1), ...segment];
|
|
145
149
|
}
|
|
146
150
|
}
|
|
147
|
-
if (valid
|
|
148
|
-
|
|
149
|
-
|
|
151
|
+
if (valid) {
|
|
152
|
+
testedPermutations.push({ path: totalPath, length: totalPath.length, tour });
|
|
153
|
+
// Add visualization step for this permutation test
|
|
154
|
+
for (const nodeId of totalPath) {
|
|
155
|
+
permutationSteps.push({
|
|
156
|
+
nodeId,
|
|
157
|
+
type: 'permutation-test',
|
|
158
|
+
permutationIndex: permIndex,
|
|
159
|
+
permutationPath: totalPath,
|
|
160
|
+
permutationLength: totalPath.length,
|
|
161
|
+
isBest: false
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
if (totalPath.length < shortestLength) {
|
|
165
|
+
shortestPath = totalPath;
|
|
166
|
+
shortestLength = totalPath.length;
|
|
167
|
+
bestPermIndex = permIndex;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
// Mark the best permutation
|
|
172
|
+
if (bestPermIndex >= 0) {
|
|
173
|
+
for (const nodeId of shortestPath) {
|
|
174
|
+
permutationSteps.push({
|
|
175
|
+
nodeId,
|
|
176
|
+
type: 'permutation-best',
|
|
177
|
+
permutationIndex: bestPermIndex,
|
|
178
|
+
permutationPath: shortestPath,
|
|
179
|
+
permutationLength: shortestLength,
|
|
180
|
+
isBest: true
|
|
181
|
+
});
|
|
150
182
|
}
|
|
151
183
|
}
|
|
152
|
-
return { optimalPath: shortestPath, allPaths };
|
|
184
|
+
return { optimalPath: shortestPath, allPaths, permutationSteps, testedPermutations };
|
|
153
185
|
}
|
|
@@ -123,12 +123,42 @@
|
|
|
123
123
|
new Set(steps.slice(0, currentStepIndex).filter((s) => s.type === 'path').map((s) => s.nodeId))
|
|
124
124
|
);
|
|
125
125
|
|
|
126
|
+
const permutationTestNodes = $derived(
|
|
127
|
+
new Set(
|
|
128
|
+
steps
|
|
129
|
+
.slice(0, currentStepIndex)
|
|
130
|
+
.filter((s) => s.type === 'permutation-test')
|
|
131
|
+
.map((s) => s.nodeId)
|
|
132
|
+
)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const permutationBestNodes = $derived(
|
|
136
|
+
new Set(
|
|
137
|
+
steps
|
|
138
|
+
.slice(0, currentStepIndex)
|
|
139
|
+
.filter((s) => s.type === 'permutation-best')
|
|
140
|
+
.map((s) => s.nodeId)
|
|
141
|
+
)
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const currentPermutationInfo = $derived(() => {
|
|
145
|
+
const currentStep = steps[currentStepIndex - 1];
|
|
146
|
+
if (currentStep?.type === 'permutation-test' || currentStep?.type === 'permutation-best') {
|
|
147
|
+
return {
|
|
148
|
+
index: currentStep.permutationIndex ?? 0,
|
|
149
|
+
length: currentStep.permutationLength ?? 0,
|
|
150
|
+
isBest: currentStep.isBest ?? false
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return null;
|
|
154
|
+
});
|
|
155
|
+
|
|
126
156
|
function runAlgorithm() {
|
|
127
157
|
if (showMultiGoal && computedGoalNodes.length > 1) {
|
|
128
158
|
const result = findOptimalMultiGoalPath(graph, computedStartNode, computedGoalNodes);
|
|
129
159
|
optimalPath = result.optimalPath;
|
|
130
160
|
|
|
131
|
-
//
|
|
161
|
+
// First show BFS exploration for each pair
|
|
132
162
|
steps = [];
|
|
133
163
|
const tour = [computedStartNode, ...computedGoalNodes];
|
|
134
164
|
|
|
@@ -136,6 +166,9 @@
|
|
|
136
166
|
const segmentResult = bidirectionalBFS(graph, tour[i], tour[i + 1]);
|
|
137
167
|
steps.push(...segmentResult.steps);
|
|
138
168
|
}
|
|
169
|
+
|
|
170
|
+
// Then show permutation comparison phase
|
|
171
|
+
steps.push(...result.permutationSteps);
|
|
139
172
|
} else {
|
|
140
173
|
const result = bidirectionalBFS(graph, computedStartNode, computedGoalNodes[0]);
|
|
141
174
|
steps = result.steps;
|
|
@@ -198,6 +231,8 @@
|
|
|
198
231
|
function getNodeColor(nodeId: string): string {
|
|
199
232
|
if (nodeId === computedStartNode) return colorScheme.start;
|
|
200
233
|
if (computedGoalNodes.includes(nodeId)) return colorScheme.end;
|
|
234
|
+
if (permutationBestNodes.has(nodeId)) return '#10b981'; // Green for best permutation
|
|
235
|
+
if (permutationTestNodes.has(nodeId)) return '#fbbf24'; // Yellow for testing permutation
|
|
201
236
|
if (pathNodes.has(nodeId)) return colorScheme.path;
|
|
202
237
|
if (nodeId === intersectionNode) return '#a855f7';
|
|
203
238
|
if (nodeId === currentForwardNode) return colorScheme.current;
|
|
@@ -252,7 +287,20 @@
|
|
|
252
287
|
</script>
|
|
253
288
|
|
|
254
289
|
<div class="graph-container" style={cssVars}>
|
|
255
|
-
|
|
290
|
+
{#if currentPermutationInfo()}
|
|
291
|
+
<div class="permutation-info">
|
|
292
|
+
{#if currentPermutationInfo()?.isBest}
|
|
293
|
+
<span class="font-mono text-sm font-bold" style="color: #10b981;">
|
|
294
|
+
✓ Best permutation found! Length: {currentPermutationInfo()?.length}
|
|
295
|
+
</span>
|
|
296
|
+
{:else}
|
|
297
|
+
<span class="font-mono text-sm">
|
|
298
|
+
Testing permutation #{(currentPermutationInfo()?.index ?? 0) + 1} - Length: {currentPermutationInfo()?.length}
|
|
299
|
+
</span>
|
|
300
|
+
{/if}
|
|
301
|
+
</div>
|
|
302
|
+
{/if}
|
|
303
|
+
<svg width={svgWidth} height={svgHeight} class="graph-svg">
|
|
256
304
|
<defs>
|
|
257
305
|
<marker
|
|
258
306
|
id="arrowhead"
|
|
@@ -315,4 +363,19 @@
|
|
|
315
363
|
gap: 1rem;
|
|
316
364
|
align-items: center;
|
|
317
365
|
}
|
|
366
|
+
|
|
367
|
+
.graph-svg {
|
|
368
|
+
background-color: white;
|
|
369
|
+
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
370
|
+
border: 2px solid #e5e7eb;
|
|
371
|
+
border-radius: 0.5rem;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.permutation-info {
|
|
375
|
+
padding: 0.75rem 1.5rem;
|
|
376
|
+
background-color: #f9fafb;
|
|
377
|
+
border: 2px solid #1f2937;
|
|
378
|
+
border-radius: 0.25rem;
|
|
379
|
+
text-align: center;
|
|
380
|
+
}
|
|
318
381
|
</style>
|
package/dist/types.d.ts
CHANGED
|
@@ -72,9 +72,13 @@ export interface GeneralGraph {
|
|
|
72
72
|
}
|
|
73
73
|
export interface BFSStep {
|
|
74
74
|
nodeId: string;
|
|
75
|
-
type: 'start-forward' | 'start-backward' | 'goal-forward' | 'goal-backward' | 'visited-forward' | 'visited-backward' | 'path' | 'current-forward' | 'current-backward' | 'intersection';
|
|
75
|
+
type: 'start-forward' | 'start-backward' | 'goal-forward' | 'goal-backward' | 'visited-forward' | 'visited-backward' | 'path' | 'current-forward' | 'current-backward' | 'intersection' | 'permutation-test' | 'permutation-best';
|
|
76
76
|
side?: 'forward' | 'backward';
|
|
77
77
|
level?: number;
|
|
78
|
+
permutationIndex?: number;
|
|
79
|
+
permutationPath?: string[];
|
|
80
|
+
permutationLength?: number;
|
|
81
|
+
isBest?: boolean;
|
|
78
82
|
}
|
|
79
83
|
export interface BFSResult {
|
|
80
84
|
path: string[];
|