@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 (const perm of goalPermutations) {
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 && totalPath.length < shortestLength) {
148
- shortestPath = totalPath;
149
- shortestLength = totalPath.length;
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
- // Generate visualization steps by running BFS for each segment
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
- <svg width={svgWidth} height={svgHeight}>
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[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hayro_o7/labyrinth",
3
- "version": "0.0.8",
3
+ "version": "0.0.9",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "dev": "vite dev",