awkrs 0.4.14

Awk implementation in Rust with broad CLI compatibility, parallel records, and experimental Cranelift JIT
Documentation
# A* shortest-path on an ASCII grid (4-neighbour moves) with Manhattan h.
# Input: a rectangular grid. '#' = wall, '.' = open, 'S' = start, 'E' = goal.
# Output: maze with path overlaid in '*', then "STEPS: <n>" (or "NO PATH").
# Open set is a min-heap keyed on (f, g, h, position) for deterministic tie
# breaks; closed set is a sparse SUBSEP array.

function abs1(x) { return (x < 0) ? -x : x }
function in_bounds(r, c) { return (r >= 1 && r <= R && c >= 1 && c <= C) }
function pkey(r, c) { return r SUBSEP c }

# Min-heap by (f, g, h, key) lex tuple.
function hpush(f, g, h, key,   i, p, tf, tg, th, tk) {
  hn++
  hf[hn] = f; hg[hn] = g; hh[hn] = h; hk[hn] = key
  i = hn
  while (i > 1) {
    p = int(i / 2)
    if (hf[p] > hf[i] \
        || (hf[p] == hf[i] && hg[p] > hg[i]) \
        || (hf[p] == hf[i] && hg[p] == hg[i] && hh[p] > hh[i]) \
        || (hf[p] == hf[i] && hg[p] == hg[i] && hh[p] == hh[i] && hk[p] > hk[i])) {
      tf = hf[p]; tg = hg[p]; th = hh[p]; tk = hk[p]
      hf[p] = hf[i]; hg[p] = hg[i]; hh[p] = hh[i]; hk[p] = hk[i]
      hf[i] = tf;    hg[i] = tg;    hh[i] = th;    hk[i] = tk
      i = p
    } else { break }
  }
}

function less(a, b) {
  if (hf[a] != hf[b]) return hf[a] < hf[b]
  if (hg[a] != hg[b]) return hg[a] < hg[b]
  if (hh[a] != hh[b]) return hh[a] < hh[b]
  return hk[a] < hk[b]
}

function hpop(   l, r, sm, tf, tg, th, tk, i) {
  pop_f = hf[1]; pop_g = hg[1]; pop_h = hh[1]; pop_k = hk[1]
  hf[1] = hf[hn]; hg[1] = hg[hn]; hh[1] = hh[hn]; hk[1] = hk[hn]
  delete hf[hn]; delete hg[hn]; delete hh[hn]; delete hk[hn]
  hn--
  i = 1
  while (1) {
    l = 2 * i; r = l + 1; sm = i
    if (l <= hn && less(l, sm)) sm = l
    if (r <= hn && less(r, sm)) sm = r
    if (sm == i) break
    tf = hf[i]; tg = hg[i]; th = hh[i]; tk = hk[i]
    hf[i] = hf[sm]; hg[i] = hg[sm]; hh[i] = hh[sm]; hk[i] = hk[sm]
    hf[sm] = tf;    hg[sm] = tg;    hh[sm] = th;    hk[sm] = tk
    i = sm
  }
}

{
  R++
  grid[R] = $0
  if (length($0) > C) C = length($0)
  for (c = 1; c <= length($0); c++) {
    ch = substr($0, c, 1)
    if (ch == "S") { sr = R; sc = c }
    if (ch == "E") { er = R; ec = c }
  }
}

END {
  dr[1] = -1; dc[1] = 0
  dr[2] = 0;  dc[2] = 1
  dr[3] = 1;  dc[3] = 0
  dr[4] = 0;  dc[4] = -1

  g_score[pkey(sr, sc)] = 0
  h0 = abs1(sr - er) + abs1(sc - ec)
  hn = 0
  hpush(h0, 0, h0, pkey(sr, sc))

  found = 0
  while (hn > 0) {
    hpop()
    cur = pop_k; curg = pop_g
    if (cur in closed) continue
    closed[cur] = 1
    split(cur, rc, SUBSEP); r = rc[1]; c = rc[2]
    if (r == er && c == ec) { found = 1; break }
    for (k = 1; k <= 4; k++) {
      nr = r + dr[k]; nc = c + dc[k]
      if (!in_bounds(nr, nc)) continue
      ch = substr(grid[nr], nc, 1)
      if (ch == "#") continue
      nk = pkey(nr, nc)
      tentative = curg + 1
      if ((nk in g_score) && tentative >= g_score[nk]) continue
      g_score[nk] = tentative
      parent[nk] = cur
      hnow = abs1(nr - er) + abs1(nc - ec)
      hpush(tentative + hnow, tentative, hnow, nk)
    }
  }

  if (!found) {
    for (i = 1; i <= R; i++) print grid[i]
    print "STEPS: NO PATH"
    exit 0
  }

  cur = pkey(er, ec)
  while (cur != "") {
    on_path[cur] = 1
    cur = parent[cur]
  }

  for (i = 1; i <= R; i++) {
    line = ""
    L = length(grid[i])
    for (c = 1; c <= L; c++) {
      ch = substr(grid[i], c, 1)
      if (ch == "S" || ch == "E") { line = line ch; continue }
      if (pkey(i, c) in on_path) { line = line "*"; continue }
      line = line ch
    }
    print line
  }
  printf "STEPS: %d\n", g_score[pkey(er, ec)]
}