← Back to Blog

Explore Mode Routing: Paths That Prioritize Discovery Over Speed

In EVE Frontier, the shortest path isn't always the most interesting path. Experienced explorers know that slightly longer routes often reveal:

Explore Mode is a routing option that trades raw efficiency for discovery potential—paths are enriched with corridor detours and overhead budget for scanning systems you haven't fully mapped yet.

This post explains the pathfinding algorithm behind Explore Mode, when to use it, and how it differs from traditional shortest-path routing.

The Problem: Efficient Routes Miss Opportunities

Standard Routing: Fastest Path Only

Traditional pathfinding (A*, Dijkstra) optimizes for one metric:

Route output (standard):

Jita → Amarr (15 jumps, fuel-optimized)

1. Jita
2. Perimeter
3. Sobaseki
... (12 direct hops)
15. Amarr

This route is fast, but you skip 20+ adjacent systems that might contain:

Result: You reach the destination quickly, but miss exploration opportunities.

Explorer Problem: I Don't Know What I'm Missing

As an explorer, you want to:

  1. Reach the destination (primary goal)
  2. Discover new sites along the way (secondary goal)

Standard routing ignores goal #2—it assumes you only care about arrival time/cost.

The Solution: Explore Mode

Explore Mode adds two new concepts to pathfinding:

  1. Corridor Factor: Tolerance for path detours that pass through interesting "corridor regions"
  2. Overhead Budget: Extra jumps allowed beyond the shortest path to enable scanning side systems

Route output (Explore Mode):

Jita → Amarr (20 jumps, discovery-enriched)

1. Jita
2. Perimeter
3. [Corridor] → Detour to Suroken (wormhole activity detected)
4. Sobaseki
5. [Overhead] → Side trip to Uedama (asteroid belts)
... (15 more hops, 3 corridor detours)
20. Amarr

Trade-off: 33% more jumps, but you scan 8 additional systems and discover 2 wormholes + 1 rare ore belt.

How It Works: Enriched Pathfinding

Step 1: Calculate Base Route

First, we calculate the standard shortest path (A* or Dijkstra):

function findShortestPath(start: string, end: string): System[] {
  const open = new PriorityQueue<Node>();
  const closed = new Set<string>();
  
  open.enqueue({ id: start, g: 0, f: heuristic(start, end) });
  
  while (!open.isEmpty()) {
    const current = open.dequeue();
    
    if (current.id === end) {
      return reconstructPath(current);
    }
    
    closed.add(current.id);
    
    for (const neighbor of getNeighbors(current.id)) {
      if (closed.has(neighbor.id)) continue;
      
      const g = current.g + edgeCost(current.id, neighbor.id);
      const f = g + heuristic(neighbor.id, end);
      
      open.enqueue({ id: neighbor.id, g, f, parent: current });
    }
  }
  
  return []; // No path found
}

Output: [Jita, Perimeter, Sobaseki, ..., Amarr] (15 systems)

This is the baseline.

Step 2: Identify Corridor Regions

Next, we identify corridor regions—spatial zones along the base route that contain interesting systems.

Corridor definition: A system is in a corridor if:

Corridor score formula:

function corridorScore(system: System): number {
  let score = 0;
  
  // Wormhole connections (+0.3 per wormhole)
  score += system.wormholeCount * 0.3;
  
  // Resource richness (+0.2 per resource type)
  score += system.asteroidBelts * 0.1;
  score += system.gasClouds * 0.1;
  
  // Player structures (+0.1 per structure)
  score += system.structureCount * 0.1;
  
  // Traffic (low traffic = more discoveries)
  score += (1.0 - system.trafficIndex) * 0.2;
  
  return Math.min(score, 1.0); // Cap at 1.0
}

Example corridor systems (Jita → Amarr route):

Corridor map:

Base Route:  Jita ─→ Perimeter ─→ Sobaseki ─→ ... ─→ Amarr
Corridors:      ↓         ↓             ↓
             Suroken   (none)        Uedama

Step 3: Enrich Path with Corridor Detours

Now we enrich the base route by inserting corridor detours:

function enrichPath(basePath: System[], corridorFactor: number): System[] {
  const enriched = [...basePath];
  
  for (let i = 0; i < basePath.length - 1; i++) {
    const current = basePath[i];
    const next = basePath[i + 1];
    
    // Find corridor systems between current and next
    const corridors = findCorridorSystems(current, next);
    
    for (const corridor of corridors) {
      // Evaluate detour cost vs discovery value
      const detourCost = pathLength(current, corridor) + pathLength(corridor, next);
      const directCost = pathLength(current, next);
      const extraCost = detourCost - directCost;
      
      const discoveryValue = corridorScore(corridor);
      
      // Corridor factor controls tolerance for extra cost
      if (extraCost <= corridorFactor && discoveryValue >= 0.5) {
        // Insert corridor detour
        enriched.splice(i + 1, 0, corridor);
        i++; // Skip inserted system
      }
    }
  }
  
  return enriched;
}

Corridor factor interpretation:

Example (corridorFactor=0.5):

Base:      Jita → Perimeter (1 jump)
Corridor:  Suroken (score 0.8)

Detour cost: Jita → Suroken → Perimeter = 2 jumps
Direct cost: Jita → Perimeter = 1 jump
Extra cost:  2 - 1 = 1 jump (100% overhead)

Decision:    1 jump ≤ 0.5? NO → Skip detour
Base:      Perimeter → Sobaseki (1 jump)
Corridor:  Suroken (score 0.8)

Detour cost: Perimeter → Suroken → Sobaseki = 2 jumps
Direct cost: Perimeter → Sobaseki = 1 jump
Extra cost:  2 - 1 = 1 jump (100% overhead)

Decision:    1 jump ≤ 0.5? NO → Skip detour

Adjusted (corridorFactor=1.0):

Decision:    1 jump ≤ 1.0? YES → Insert Suroken

Result: Path becomes [Jita, Perimeter, Suroken, Sobaseki, ..., Amarr].

Step 4: Apply Overhead Budget

After corridor enrichment, we apply the overhead budget—extra jumps allowed for side scanning.

Overhead budget formula:

allowed_overhead = base_path_length * overhead_factor

Example:

Base path:       15 jumps
Overhead factor: 0.3 (30%)
Allowed overhead: 15 * 0.3 = 4.5 jumps ≈ 5 jumps

After corridor enrichment:

Enriched path: 17 jumps (2 corridor detours)
Used overhead: 17 - 15 = 2 jumps
Remaining:     5 - 2 = 3 jumps

We can add 3 more exploration hops if valuable systems exist along the route.

Code:

function applyOverheadBudget(
  enrichedPath: System[],
  basePath: System[],
  overheadFactor: number
): System[] {
  const allowedOverhead = Math.floor(basePath.length * overheadFactor);
  const usedOverhead = enrichedPath.length - basePath.length;
  const remainingBudget = allowedOverhead - usedOverhead;
  
  if (remainingBudget <= 0) return enrichedPath;
  
  // Find high-value adjacent systems not on route
  const candidates = findAdjacentSystems(enrichedPath)
    .filter(s => !enrichedPath.includes(s))
    .sort((a, b) => corridorScore(b) - corridorScore(a));
  
  // Add top candidates within budget
  const final = [...enrichedPath];
  for (let i = 0; i < Math.min(candidates.length, remainingBudget); i++) {
    final.push(candidates[i]);
  }
  
  return final;
}

Final path:

Jita → Perimeter → Suroken → Sobaseki → Uedama → ... → Amarr
(20 jumps: 15 base + 2 corridor + 3 overhead)

UI Controls: Tuning Discovery vs Speed

The Explore Mode panel exposes two sliders:

Corridor Factor Slider

┌─────────────────────────────────────┐
│ Corridor Factor: 1.0                │
│ ●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 0.0              1.0              2.0│
└─────────────────────────────────────┘

Recommended values:

Overhead Budget Slider

┌─────────────────────────────────────┐
│ Overhead Budget: 30%                │
│ ●━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ 0%               30%              50%│
└─────────────────────────────────────┘

Recommended values:

Performance: Caching and Optimization

Problem: Corridor Scoring is Expensive

For a 15-jump route with 500 adjacent systems, calculating corridor scores for all candidates:

500 systems × 5 metrics each = 2,500 computations

This takes ~50ms on desktop, ~200ms on mobile—too slow for interactive routing.

Solution: Spatial Grid Caching

We pre-compute corridor scores for all systems and store them in a spatial grid:

const corridorScoreCache = new Map<string, number>();

function getCachedCorridorScore(systemId: string): number {
  if (!corridorScoreCache.has(systemId)) {
    corridorScoreCache.set(systemId, corridorScore(systems[systemId]));
  }
  return corridorScoreCache.get(systemId)!;
}

Result: Corridor score lookup is O(1) instead of O(metrics).

Spatial Grid: Finding Adjacent Systems Fast

Instead of scanning all 8,000 systems to find "systems within 3 jumps of route":

const spatialGrid = new Map<string, System[]>();

function buildSpatialGrid(cellSize: number) {
  spatialGrid.clear();
  
  for (const system of Object.values(systems)) {
    const cellKey = getCellKey(system.x, system.y, system.z, cellSize);
    
    if (!spatialGrid.has(cellKey)) {
      spatialGrid.set(cellKey, []);
    }
    
    spatialGrid.get(cellKey)!.push(system);
  }
}

function findAdjacentSystems(routeSystem: System): System[] {
  const cellKey = getCellKey(routeSystem.x, routeSystem.y, routeSystem.z, cellSize);
  
  // Check current cell + 26 neighboring cells
  const neighbors = [];
  for (const offset of neighborOffsets) {
    const neighborKey = offsetCellKey(cellKey, offset);
    if (spatialGrid.has(neighborKey)) {
      neighbors.push(...spatialGrid.get(neighborKey)!);
    }
  }
  
  return neighbors;
}

Result: Finding adjacent systems is O(k) (average ~100 systems per cell) instead of O(n) (8,000 systems).

Total speedup: 50ms → 3ms on desktop, 200ms → 12ms on mobile.

Use Cases

Use Case 1: Wormhole Hunter

Goal: Travel Jita → Amarr, but scan for wormholes along the way.

Settings:

Route:

Standard: 15 jumps
Explore:  23 jumps (15 + 8 exploration)

Discoveries:
- 3 wormholes (Suroken, Uedama, Pashanai)
- 1 rare ore belt (Hek)
- 2 data sites (Rancer, Dodixie)

Result: 50% longer trip, but 3 wormholes found (each worth 50M+ ISK).

Use Case 2: Territory Mapper

Goal: Map all systems in a region for alliance intel.

Settings:

Route:

Start:    System A
End:      System Z
Standard: 12 jumps
Explore:  24 jumps (12 + 12 exploration)

Coverage:
- Standard: 12 systems visited
- Explore:  24 systems visited (100% increase)

Result: Alliance now has intel on 24 systems instead of 12.

Use Case 3: Casual Explorer

Goal: Reach destination, but discover a few interesting systems.

Settings:

Route:

Standard: 15 jumps
Explore:  18 jumps (15 + 3 exploration)

Discoveries:
- 1 wormhole
- 1 asteroid belt

Result: Only 20% longer trip, but still discovered 1 wormhole.

Corridor Definitions: Where to Explore

High-Value Corridors

Wormhole-rich regions (score 0.7-1.0):

Low-security corridors (score 0.6-0.8):

Resource-rich corridors (score 0.5-0.7):

Low-Value Corridors (Skip)

High-traffic trade hubs (score <0.3):

Dead-end systems (score <0.2):

PvP hotspots (optional skip):

Explore Mode vs Standard Routing

Feature Standard Routing Explore Mode
Goal Minimize travel cost Maximize discovery + reach destination
Path Shortest (A*, Dijkstra) Enriched with detours
Overhead 0% 20-50% (configurable)
Computation ~5ms ~12ms (optimized)
Discoveries 0 side systems 3-10 side systems
Use Case Time-sensitive travel Exploration, mapping

Algorithm Complexity

Standard A*:

Time:  O(E log V)  (E=edges, V=vertices)
Space: O(V)

Explore Mode (A* + enrichment):

Time:  O(E log V + C*V)  (C=corridor factor, ~3)
Space: O(V + k*V)        (k=spatial grid cells per system, ~27)

Practical performance (Jita → Amarr):

Slowdown: 2.4x, acceptable for interactive use.

Troubleshooting

"Explore route is too long"

Cause: Corridor factor or overhead budget too high.

Fix: Reduce corridor factor to 0.5 or overhead budget to 20%.

"No extra systems added"

Cause: No high-value corridor systems along route, or corridor factor too low.

Fix: Increase corridor factor to 1.5+ or overhead budget to 40%+.

"Performance lag on mobile"

Cause: Spatial grid not built for current jump range.

Fix: Ensure spatial grid is initialized (automatic in production builds).

Future Enhancements

Planned Features

Community Requests

Related Posts

Explore Mode turns every route into a discovery expedition—sacrificing a bit of speed for the chance to find wormholes, resources, and strategic intel you'd otherwise miss!

routingpathfindingexplorationcorridor factorsdiscovery mode