Route OptimizerPhase 2

Waiting for Map...
*accept Only file .JSON

Algorithm



Code Display
/**
 * Dijkstra Algorithm Class
 * This class handles the logic for finding the shortest path in a weighted graph.
 * It converts raw JSON data into an Adjacency List and executes the algorithm.
 */
class Dijkstra {
  /**
   * Constructor
   * @param {Object} graphData - The JSON object containing nodes, edges, startNode, and endNode.
   */
  constructor(graphData) {
    // Note: We use 'this.' to define class properties. Unlike 'let' variables which are temporary to the function,
    // 'this.' properties persist and can be accessed in other methods like run().

    // We use a Map for the adjacency list for better performance (O(1) access time)
    this.adjacencyList = new Map();

    // Capture Start and End nodes from JSON configuration
    this.defaultStart = graphData.startNode || null;
    this.defaultEnd = graphData.endNode || null;

    // 1. Initialize all nodes in the adjacency list with empty arrays
    if (graphData.nodes) {
      graphData.nodes.forEach((node) => {
        this.adjacencyList.set(node.id, []);
      });
    }

    // 2. Populate the adjacency list with edges (Connections)
    if (graphData.edges) {
      graphData.edges.forEach((edge) => {
        // Check if the 'from' node exists to avoid errors
        if (this.adjacencyList.has(edge.from)) {
          this.adjacencyList.get(edge.from).push({
            to: edge.to,
            weight: edge.weight,
          });
        }
      });
    }
  }

  /**
   * Helper Method: getLowestCostNode
   * Finds the unvisited node with the smallest known distance.
   * This represents the "Greedy" step of the algorithm.
   * @param {Map} distances - Current known distances.
   * @param {Set} unvisited - Set of nodes that haven't been processed yet.
   * @returns {String|null} - The ID of the node with the lowest cost.
   */
  getLowestCostNode(distances, unvisited) {
    let lowestNode = null;
    let lowestValue = Infinity;

    // Iterate through unvisited nodes to find the minimum distance
    for (let nodeId of unvisited) {
      let dist = distances.get(nodeId);
      if (dist < lowestValue) {
        lowestValue = dist;
        lowestNode = nodeId;
      }
    }
    return lowestNode;
  }

  /**
   * Main Method: run
   * Executes Dijkstra's algorithm from a start node to an end node.
   * Uses defaults from JSON if arguments are not provided.
   * @param {String} [startNodeId] - Optional override for start node.
   * @param {String} [endNodeId] - Optional override for destination node.
   * @returns {Object} - Contains found status, path array, total cost, and execution logs.
   */
  run(startNodeId = this.defaultStart, endNodeId = this.defaultEnd) {
    let logs = []; // Records steps for visualization/debugging

    // VALIDATION: Ensure start and end nodes are defined
    if (!startNodeId || !endNodeId) {
      logs.push("Error: Start or End node not defined in JSON or arguments.");
      return { found: false, path: [], cost: 0, logs: logs };
    }

    // VALIDATION: Ensure nodes exist in the graph
    if (
      !this.adjacencyList.has(startNodeId) ||
      !this.adjacencyList.has(endNodeId)
    ) {
      logs.push(
        `Error: Nodes ${startNodeId} or ${endNodeId} do not exist in the graph.`
      );
      return { found: false, path: [], cost: 0, logs: logs };
    }

    // --- Data Structures ---
    let distances = new Map(); // Stores the shortest distance from start to each node
    let previous = new Map(); // Stores the path history (breadcrumbs)
    let unvisited = new Set(); // Tracks nodes that need to be processed

    // --- Initialization ---
    // Set all distances to Infinity and add all nodes to the unvisited set
    for (let nodeId of this.adjacencyList.keys()) {
      distances.set(nodeId, Infinity);
      previous.set(nodeId, null);
      unvisited.add(nodeId);
    }

    // The distance to the start node is always 0
    distances.set(startNodeId, 0);
    logs.push(`Initial state: Start at [${startNodeId}] with distance 0`);

    // --- Main Algorithm Loop ---
    while (unvisited.size > 0) {
      // Step 1: Select the unvisited node with the lowest distance
      let currentNode = this.getLowestCostNode(distances, unvisited);

      // STOP CONDITION 1: No reachable nodes left (or graph is disconnected)
      if (currentNode === null || distances.get(currentNode) === Infinity) {
        break;
      }

      // STOP CONDITION 2: Target reached
      if (currentNode === endNodeId) {
        logs.push(`Target [${endNodeId}] reached!`);
        break;
      }

      // Step 2: Mark the current node as visited
      logs.push(
        `Visiting node [${currentNode}] with current cost ${distances.get(
          currentNode
        )}`
      );
      unvisited.delete(currentNode);

      // Step 3: Explore Neighbors (Relaxation Step)
      let neighbors = this.adjacencyList.get(currentNode) || [];

      for (let neighbor of neighbors) {
        // Skip nodes that have already been fully processed
        if (!unvisited.has(neighbor.to)) continue;

        // Calculate potential new distance
        let newDist = distances.get(currentNode) + neighbor.weight;
        let currentNeighborDist = distances.get(neighbor.to);

        // If we found a shorter path, update the records
        if (newDist < currentNeighborDist) {
          distances.set(neighbor.to, newDist);
          previous.set(neighbor.to, currentNode); // Point back to where we came from

          logs.push(
            `Updated [${neighbor.to}]: old cost ${currentNeighborDist} -> new cost ${newDist} (via ${currentNode})`
          );
        }
      }
    }

    // --- Path Reconstruction (Backtracking) ---
    let path = [];
    let current = endNodeId;

    // Check if the destination is still unreachable (Infinity)
    if (distances.get(endNodeId) === Infinity) {
      return { found: false, path: [], cost: 0, logs: logs };
    }

    // Backtrack from End to Start using the 'previous' map
    while (current !== null) {
      path.unshift(current); // Add to the beginning of the array
      current = previous.get(current);
    }

    return {
      found: true,
      path: path,
      cost: distances.get(endNodeId),
      logs: logs,
    };
  }
}
Execution Log
> System initialized.
> Waiting for map data...
> Ready.