Theta* (Any-angle)

Theta* (Any-angle) pathfinding algorithm — interactive grid visualization, pseudocode in five languages, and complexity reference.

Time O((V + E) log V) Weighted Heuristic Informed

Implementation

function thetastarPathfind(grid, start, end, allowDiag, hFn) {
  const h = hFn || HEURISTICS.octile;
  const rows = grid.length;
  const cols = grid[0].length;
  const gScore = Matrix(rows, cols, Infinity);
  const parent = new Map();
  const open = PriorityQueue("f");
  function lineOfSight(p1, p2) {
    let __pattern1 = p1;
    let r0 = __pattern1[0];
    let c0 = __pattern1[1];
    const __pattern2 = p2;
    const r1Start = __pattern2[0];
    const c1Start = __pattern2[1];
    let r1 = r1Start;
    let c1 = c1Start;
    const dr = Math.abs(r1 - r0);
    const dc = Math.abs(c1 - c0);
    const sr = r0 < r1 ? 1 : -1;
    const sc = c0 < c1 ? 1 : -1;
    let err = dc - dr;
    while (true) {
      if (grid[r0][c0] === WALL) return false;
      if (r0 === r1 && c0 === c1) break;
      const e2 = err * 2;
      if (e2 > -dr) {
        err -= dr;
        c0 += sc;
      }
      if (e2 < dc) {
        err += dc;
        r0 += sr;
      }
    }
    return true;
  }
  gScore[start[0]][start[1]] = 0;
  parent.set(key(start[0], start[1]), start);
  priorityQueuePush(open, {
    f: h(start[0], start[1], end[0], end[1]),
    r: start[0],
    c: start[1]
  });
  while (priorityQueueSize(open)) {
    const __pattern3 = priorityQueuePop(open);
    const r = __pattern3.r;
    const c = __pattern3.c;
    visitNode(r, c);
    if (r === end[0] && c === end[1]) {
      reconstructPath(parent, start, end);
      return;
    }
    for (const [nr, nc] of getNeighbors(grid, r, c, allowDiag)) {
      const parentKey = parent.get(key(r, c));
      const anchor = parentKey || [r, c];
      const canSee = lineOfSight(anchor, [nr, nc]);
      const tentative = gScore[anchor[0]][anchor[1]] + Math.hypot(nr - anchor[0], nc - anchor[1]);
      if (canSee && tentative < gScore[nr][nc]) {
        gScore[nr][nc] = tentative;
        parent.set(key(nr, nc), anchor);
        priorityQueuePush(open, {
          f: tentative + h(nr, nc, end[0], end[1]),
          r: nr,
          c: nc
        });
        pushFrontier(nr, nc);
      } else {
        const alt = gScore[r][c] + stepCost(grid, r, c, nr, nc);
        if (alt < gScore[nr][nc]) {
          gScore[nr][nc] = alt;
          parent.set(key(nr, nc), [r, c]);
          priorityQueuePush(open, {
            f: alt + h(nr, nc, end[0], end[1]),
            r: nr,
            c: nc
          });
          pushFrontier(nr, nc);
        }
      }
    }
  }
  reportNoPath();
}