Recursive Best-First

Recursive Best-First pathfinding algorithm — interactive grid visualization, pseudocode in five languages, and complexity reference.

Time O(branch^depth) worst case Weighted Heuristic Informed

Implementation

function rbfsPathfind(grid, start, end, allowDiag, hFn) {
  const h = hFn || HEURISTICS.manhattan;
  const prev = new Map();
  const startKey = key(start[0], start[1]);
  function rbfs(node, fLimit, pathVisited) {
    const __pattern1 = node;
    const r = __pattern1.r;
    const c = __pattern1.c;
    const g = __pattern1.g;
    const f = __pattern1.f;
    visitNode(r, c);
    if (r === end[0] && c === end[1]) {
      return {
        f,
        success: true
      };
    }
    const neighbors = [];
    for (const [nr, nc] of getNeighbors(grid, r, c, allowDiag)) {
      const neighborKey = key(nr, nc);
      if (pathVisited.has(neighborKey)) continue;
      const g2 = g + stepCost(grid, r, c, nr, nc);
      const f2 = Math.max(g2 + h(nr, nc, end[0], end[1]), f);
      neighbors.push({
        r: nr,
        c: nc,
        g: g2,
        f: f2,
        key: neighborKey
      });
      pushFrontier(nr, nc);
    }
    if (!neighbors.length) return {
      f: Infinity,
      success: false
    };
    while (neighbors.length) {
      sortByField(neighbors, "f");
      const best = neighbors[0];
      const alternative = arrayPeekValue(neighbors, 1, "f", Infinity);
      if (best.f > fLimit) return {
        f: best.f,
        success: false
      };
      if (!Number.isFinite(best.f) && !Number.isFinite(alternative)) {
        return {
          f: Infinity,
          success: false
        };
      }
      prev.set(best.key, [r, c]);
      pathVisited.add(best.key);
      const result = rbfs(best, Math.min(fLimit, alternative), pathVisited);
      pathVisited.delete(best.key);
      best.f = result.f;
      if (result.success) return result;
      prev.delete(best.key);
    }
    return {
      f: Infinity,
      success: false
    };
  }
  const startNode = {
    r: start[0],
    c: start[1],
    g: 0,
    f: h(start[0], start[1], end[0], end[1])
  };
  const result = rbfs(startNode, Infinity, new Set([startKey]));
  if (result.success) {
    reconstructPath(prev, start, end);
  } else {
    reportNoPath();
  }
}