Machine Coding Problem

ETA Calculator

macoAllgeodijkstra-with-live-weights
Commonly Asked By:UberLyftGoogle

Functional Scope (In-Scope)

  • Road Graph Dijkstra Solver: Calculates routes dynamically using base segment travel times and active mode speed modifiers.
  • Dynamic Traffic Layers: Modifies edge multipliers on-the-fly to model accidents, dynamic speed boundaries, and gridlock constraints.
  • Transport Mode Enforcement: Enforces node restrictions (e.g. preventing walking routes from taking highways, or cars from taking footpaths).
  • Automatic Route Re-Routing: Tracks user coordinates, automatically calculating a fresh route from their current location if they drift > 50m for more than 10 seconds.

Explicit Boundaries (Out-of-Scope)

  • Multi-Destination Traveling Salesperson Solving: Focuses strictly on point-to-point Dijkstra paths.
  • Elevation Slope Adjustments: Excludes detailed grade vectors from time formulas to prioritize traffic and execution speed.

Production reference implementations demonstrating Dijkstra pathfinders, live traffic multipliers, and GPS route monitors in Java and Python:

// ─── JAVA BLUEPRINT ──────────────────────────────────────────────────────────
import java.util.*;
import java.util.concurrent.*;

enum TransportMode {
    CAR(1.0),
    BIKE(2.0),      // 2x slower than car base speed
    WALKING(6.0);   // 6x slower than car base speed

    private final double speedFactor;
    TransportMode(double speedFactor) {
        this.speedFactor = speedFactor;
    }
    public double getSpeedFactor() { return speedFactor; }
}

class GeoPoint {
    private final double lat;
    private final double lng;

    public GeoPoint(double lat, double lng) {
        this.lat = lat;
        this.lng = lng;
    }

    public double getLat() { return lat; }
    public double getLng() { return lng; }

    public double distanceTo(GeoPoint other) {
        double R = 6371000.0; // Earth's radius in meters
        double latDist = Math.toRadians(other.lat - this.lat);
        double lngDist = Math.toRadians(other.lng - this.lng);
        double a = Math.sin(latDist / 2) * Math.sin(latDist / 2)
                + Math.cos(Math.toRadians(this.lat)) * Math.cos(Math.toRadians(other.lat))
                * Math.sin(lngDist / 2) * Math.sin(lngDist / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c;
    }
}

class RoadNode {
    private final String nodeId;
    private final GeoPoint location;

    public RoadNode(String nodeId, GeoPoint location) {
        this.nodeId = nodeId;
        this.location = location;
    }

    public String getNodeId() { return nodeId; }
    public GeoPoint getLocation() { return location; }
}

class RoadEdge {
    private final String edgeId;
    private final String sourceId;
    private final String targetId;
    private final double baseTravelTimeSeconds;
    private final Set<TransportMode> allowedModes;
    private volatile double liveMultiplier = 1.0;

    public RoadEdge(String edgeId, String sourceId, String targetId, double baseTravelTimeSeconds, Set<TransportMode> allowedModes) {
        this.edgeId = edgeId;
        this.sourceId = sourceId;
        this.targetId = targetId;
        this.baseTravelTimeSeconds = baseTravelTimeSeconds;
        this.allowedModes = new HashSet<>(allowedModes);
    }

    public double getWeight(TransportMode mode) {
        if (!allowedModes.contains(mode)) {
            return Double.MAX_VALUE;
        }
        return baseTravelTimeSeconds * liveMultiplier * mode.getSpeedFactor();
    }

    public void setLiveMultiplier(double multiplier) {
        this.liveMultiplier = multiplier;
    }

    public String getEdgeId() { return edgeId; }
    public String getSourceId() { return sourceId; }
    public String getTargetId() { return targetId; }
    public double getBaseTravelTimeSeconds() { return baseTravelTimeSeconds; }
    public double getLiveMultiplier() { return liveMultiplier; }
    public Set<TransportMode> getAllowedModes() { return allowedModes; }
}

class RoadGraph {
    private final ConcurrentHashMap<String, RoadNode> nodes = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<String, List<RoadEdge>> adjacencyList = new ConcurrentHashMap<>();

    public void addNode(RoadNode node) {
        nodes.put(node.getNodeId(), node);
    }

    public void addEdge(RoadEdge edge) {
        adjacencyList.computeIfAbsent(edge.getSourceId(), k -> new CopyOnWriteArrayList<>()).add(edge);
    }

    public RoadNode getNode(String nodeId) {
        return nodes.get(nodeId);
    }

    public List<RoadEdge> getOutgoingEdges(String nodeId) {
        return adjacencyList.getOrDefault(nodeId, Collections.emptyList());
    }

    public ConcurrentHashMap<String, RoadNode> getNodes() { return nodes; }
}

class Route {
    private final List<String> nodeIds;
    private final double totalTimeSeconds;

    public Route(List<String> nodeIds, double totalTimeSeconds) {
        this.nodeIds = new ArrayList<>(nodeIds);
        this.totalTimeSeconds = totalTimeSeconds;
    }

    public List<String> getNodeIds() { return nodeIds; }
    public double getTotalTimeSeconds() { return totalTimeSeconds; }
}

class ETAEngine {
    private final RoadGraph graph;

    public ETAEngine(RoadGraph graph) {
        this.graph = graph;
    }

    // Dijkstra's Routing Algorithm with Dynamic Weights and Mode Filters
    public Route calculateFastestRoute(String sourceId, String targetId, TransportMode mode) {
        if (graph.getNode(sourceId) == null || graph.getNode(targetId) == null) {
            return null;
        }

        PriorityQueue<NodeState> pq = new PriorityQueue<>(Comparator.comparingDouble(n -> n.timeAccumulated));
        Map<String, Double> minTimes = new HashMap<>();
        Map<String, String> predecessors = new HashMap<>();

        minTimes.put(sourceId, 0.0);
        pq.add(new NodeState(sourceId, 0.0));

        while (!pq.isEmpty()) {
            NodeState current = pq.poll();

            if (current.timeAccumulated > minTimes.getOrDefault(current.nodeId, Double.MAX_VALUE)) {
                continue;
            }

            if (current.nodeId.equals(targetId)) {
                break; // Shortest path discovered
            }

            for (RoadEdge edge : graph.getOutgoingEdges(current.nodeId)) {
                double weight = edge.getWeight(mode);
                if (weight == Double.MAX_VALUE) continue; // Route mode blocked

                double newTime = current.timeAccumulated + weight;
                if (newTime < minTimes.getOrDefault(edge.getTargetId(), Double.MAX_VALUE)) {
                    minTimes.put(edge.getTargetId(), newTime);
                    predecessors.put(edge.getTargetId(), current.nodeId);
                    pq.add(new NodeState(edge.getTargetId(), newTime));
                }
            }
        }

        if (!minTimes.containsKey(targetId)) {
            return null; // Route unreachable
        }

        // Reconstruct shortest time path
        List<String> path = new LinkedList<>();
        String curr = targetId;
        while (curr != null) {
            path.add(0, curr);
            curr = predecessors.get(curr);
        }

        return new Route(path, minTimes.get(targetId));
    }

    private static class NodeState {
        private final String nodeId;
        private final double timeAccumulated;

        public NodeState(String nodeId, double timeAccumulated) {
            this.nodeId = nodeId;
            this.timeAccumulated = timeAccumulated;
        }
    }
}

class RouteMonitor {
    private final RoadGraph graph;
    private final ETAEngine etaEngine;
    private final String userId;
    private Route activeRoute;
    private TransportMode activeMode;
    private String targetNodeId;
    
    private volatile long deviationStartMs = 0L;
    private final double maxAllowedDeviationMeters = 50.0;
    private final long deviationThresholdMs = 10000; // 10 seconds

    public RouteMonitor(String userId, RoadGraph graph, ETAEngine etaEngine) {
        this.userId = userId;
        this.graph = graph;
        this.etaEngine = etaEngine;
    }

    public void startRoute(Route route, TransportMode mode, String destinationNodeId) {
        this.activeRoute = route;
        this.activeMode = mode;
        this.targetNodeId = destinationNodeId;
        this.deviationStartMs = 0L;
    }

    // Dynamic Deviation and Re-routing Pipeline
    public Route processGPSUpdate(GeoPoint currentGPS) {
        if (activeRoute == null) return null;

        // Calculate minimum distance from GPS coordinate to nodes on planned route
        double minDistance = Double.MAX_VALUE;
        String nearestNode = null;

        for (String nodeId : activeRoute.getNodeIds()) {
            RoadNode node = graph.getNode(nodeId);
            if (node != null) {
                double distance = currentGPS.distanceTo(node.getLocation());
                if (distance < minDistance) {
                    minDistance = distance;
                    nearestNode = nodeId;
                }
            }
        }

        // Check if user has drifted past threshold bounds
        if (minDistance > maxAllowedDeviationMeters) {
            long now = System.currentTimeMillis();
            if (deviationStartMs == 0L) {
                deviationStartMs = now;
            } else if (now - deviationStartMs >= deviationThresholdMs) {
                System.out.println("ROUTE MONITOR -> User " + userId + " deviated by " + minDistance + "m. RE-ROUTING...");
                
                // Trigger re-routing using nearest active node as new origin
                Route newRoute = etaEngine.calculateFastestRoute(nearestNode, targetNodeId, activeMode);
                if (newRoute != null) {
                    startRoute(newRoute, activeMode, targetNodeId);
                    return newRoute;
                }
            }
        } else {
            deviationStartMs = 0L; // Reset timer if returned to safe path
        }
        return null;
    }
}

public class Main {
    public static void main(String[] args) {
        System.out.println("=== JAVA ETA CALCULATOR SIMULATION ===");
        RoadGraph graph = new RoadGraph();
        
        RoadNode n1 = new RoadNode("A", new GeoPoint(40.7128, -74.0060));
        RoadNode n2 = new RoadNode("B", new GeoPoint(40.7130, -74.0070));
        RoadNode n3 = new RoadNode("C", new GeoPoint(40.7140, -74.0080));
        graph.addNode(n1);
        graph.addNode(n2);
        graph.addNode(n3);
        
        Set<TransportMode> allModes = new HashSet<>(Arrays.asList(TransportMode.values()));
        RoadEdge e1 = new RoadEdge("e-1", "A", "B", 60.0, allModes);
        RoadEdge e2 = new RoadEdge("e-2", "B", "C", 120.0, allModes);
        graph.addEdge(e1);
        graph.addEdge(e2);
        
        ETAEngine engine = new ETAEngine(graph);
        Route route = engine.calculateFastestRoute("A", "C", TransportMode.CAR);
        
        if (route != null) {
            System.out.println("Route found: " + route.getNodeIds() + " | Time: " + route.getTotalTimeSeconds() + "s");
        } else {
            System.out.println("No route found.");
        }
        
        System.out.println("=== END OF JAVA SIMULATION ===");
    }
}