Advent of Code 2021 - Day 9

If you can model how the smoke flows through the caves, you might be able to avoid it and be that much safer. The submarine generates a heightmap of the floor of the nearby caves for you (your puzzle input).

Day 9a

Feeling a bit more idealistic, I guess, wrote a lib function to reduce over a two-dimensional grid:

interface GridReduce {
  <T = any, R = any>(grid: T[][], fn: (r: R, v: T) => R, init: R): R
  indexed: <T = any, R = any>(
    grid: T[][],
    fn: (r: R, v: T, p: Point) => R,
    init: R,
  ) => R
}

const gridReduce: GridReduce = (grid, fn, init) =>
  reduce(grid, (r, vs) => reduce(vs, (r, v) => fn(r, v), r), init)

gridReduce.indexed = (grid, fn, init) =>
  reduce.indexed(
    grid,
    (r, vs, y) => reduce.indexed(vs, (r, v, x) => fn(r, v, {x, y}), r),
    init,
  )

Here’s my solution for day 9a using gridReduce:

export const d9a = ({input = inputs.d9}: {input?: string}) => {
  const width = clean(input).indexOf('\n')
  const depths = ranks(Array.from(input.replace(/\D/g, '')).map(parse10), width)
  const deltas = [-1, 1].flatMap(d => [
    {dx: d, dy: 0},
    {dx: 0, dy: d},
  ])
  const nearby = ({x, y}: Point): Point[] =>
    R.pipe(
      deltas,
      R.map(({dx, dy}) => ({x: x + dx, y: y + dy})),
      R.filter(({x, y}) => x >= 0 && x < width && y >= 0 && y < depths.length),
    )
  const depthAt = ({x, y}: Point) => depths[y][x]
  return gridReduce.indexed(
    depths,
    (risk, d, p) =>
      nearby(p)
        .map(depthAt)
        .every(s => s > d)
        ? risk + d + 1
        : risk,
    0,
  )
}

Day 9a sample:

Day 9a mine:

Day 9b

const setAdd = <T = any>(set: Set<T>, x: T) => {
  const sizeBefore = set.size
  set.add(x)
  return set.size > sizeBefore
}

export const d9b = ({input = inputs.d9}: {input?: string}) => {
  const width = clean(input).indexOf('\n')
  const depths = ranks(Array.from(input.replace(/\D/g, '')).map(parse10), width)
  const deltas = [-1, 1].flatMap(d => [
    {dx: d, dy: 0},
    {dx: 0, dy: d},
  ])
  const nearby = (
    {x, y}: Point,
    {w, h}: {w: number; h: number} = {w: width, h: depths.length},
  ): Point[] =>
    R.pipe(
      deltas,
      R.map(({dx, dy}) => ({x: x + dx, y: y + dy})),
      R.filter(({x, y}) => x >= 0 && x < w && y >= 0 && y < h),
    )
  const depthAt = ({x, y}: Point) => depths[y][x]
  const lows = gridReduce.indexed(
    depths,
    (lows, d, p) => (
      nearby(p)
        .map(depthAt)
        .every(s => s > d) && lows.push(p),
      lows
    ),
    [] as Point[],
  )
  const basinAt = (
    here: Point,
    found: Set<string> = new Set(),
  ): Set<string> => {
    for (const there of nearby(here)) {
      if (depthAt(there) < 9 && setAdd(found, `${there.x},${there.y}`)) {
        basinAt(there, found)
      }
    }
    return found
  }
  const basins = lows.map(low => basinAt(low))
  return R.pipe(
    basins.map(b => b.size).sort((a, b) => b - a),
    R.take(3),
    mult,
  )
}

Day 9b sample:

Day 9b mine: