Day 06

import Solution from "./solution.ts";

const dirs = [[1, 0, "v"], [0, -1, "<"], [-1, 0, "^"], [
  0,
  1,
  ">",
]] as const;

function getStart(arr: string[][]) {
  const [x, y] = arr.reduce((p, c, x) => {
    if (p !== undefined) return p;
    const y = c.findIndex((v) => v !== "." && v !== "#");
    return y !== -1 ? [x, y] as const : undefined;
  }, undefined as readonly [number, number] | undefined)!;
  const dir = dirs.findIndex((v) => v[2] === arr[x][y]);
  return [x, y, dir];
}

function calculatePath(
  arr: string[][],
  start = getStart(arr),
) {
  let [x, y, dir] = start;
  const visited = new Set<string>();
  const path: [number, number, number][] = [];
  while (x < arr.length && x >= 0 && y < arr[0].length && y >= 0) {
    const pos = [x, y, dir].join(",");
    if (visited.has(pos) && arr[x]?.[y] !== undefined) {
      return undefined;
    }
    path.push([x, y, dir]);
    visited.add(pos);
    const nx = x + dirs[dir][0];
    const ny = y + dirs[dir][1];
    if (arr[nx]?.[ny] === "#") {
      dir = (dir + 1) % 4;
    } else {
      x = nx;
      y = ny;
    }
  }
  return {
    visited: new Set([...visited].map((l) => l.slice(0, l.lastIndexOf(",")))),
    path,
  };
}

function getSkipMap(arr: string[][]) {
  const map = new Map<string, string | undefined>();
  const px: number[] = arr[0].map(() => -1);
  const nx: number[] = arr[0].map((_, y) =>
    arr.findIndex((l) => l[y] === "#") - 1
  );
  let start = "";
  for (let x = 0; x < arr.length; x++) {
    let py = -1;
    let ny = arr[x].indexOf("#") - 1;
    for (let y = 0; y < arr[0].length; y++) {
      if (arr[x][y] !== "." && arr[x][y] !== "#") {
        start = [x, y, dirs.findIndex((v) => v[2] === arr[x][y])].join(",");
      }
      if (arr[x][y] === "#") {
        px[y] = x + 1;
        py = y + 1;
        ny = arr[x].indexOf("#") - 1;
        nx[y] = arr.slice(x).findIndex((l) => l[y] === "#") - 1;
      }
      map.set(
        [x, y, 0].join(","),
        nx[y] < 0 ? undefined : [nx[y], y, 1].join(","),
      );
      map.set([x, y, 1].join(","), py < 0 ? undefined : [x, py, 2].join(","));
      map.set(
        [x, y, 2].join(","),
        px[y] < 0 ? undefined : [px[y], y, 3].join(","),
      );
      map.set(
        [x, y, 3].join(","),
        ny < 0 ? undefined : [x, ny, 0].join(","),
      );
    }
  }
  return { skip: map, start };
}

function getMap(arr: string[][]) {
  const skip = new Map<string, string>();
  let start = "";
  for (let x = 0; x < arr.length; x++) {
    for (let y = 0; y < arr[0].length; y++) {
      if (arr[x][y] !== "." && arr[x][y] !== "#") {
        start = [x, y, dirs.findIndex((v) => v[2] === arr[x][y])].join(",");
      }
      for (let d = 0; d < dirs.length; d++) {
        const nx = x + dirs[d][0];
        const ny = y + dirs[d][1];
        const n = arr[nx]?.[ny];
        const pos = [x, y, d].join(",");
        if (n === "#") {
          skip.set(pos, [x, y, (d + 1) % 4].join(","));
        } else if (n !== undefined) {
          skip.set(pos, [nx, ny, d].join(","));
        }
      }
    }
  }
  return { skip, start };
}

function useMap(map: { skip: Map<string, string>; start: string }) {
  const { skip, start } = map;
  const path = new Set<string>();
  for (
    let p: string | undefined = start;
    p !== undefined;
    p = skip.get(p)
  ) {
    if (path.has(p)) return undefined;
    path.add(p);
  }
  return path;
}

const task = new Solution(
  (arr: string[][]) => {
    return useMap(getMap(arr))!.values().reduce(
      (p, v) => p.add(v.slice(0, v.lastIndexOf(","))),
      new Set<string>(),
    ).size;
  },
  (arr: string[][]) => {
    let obst = 0;
    const seen = new Set<string>();
    const { path } = calculatePath(arr)!;
    path.forEach(([x, y], i, a) => {
      if (i === 0) return;
      const pos = [x, y].join(",");
      const prev = arr[x][y];
      if (seen.has(pos) || prev === "#") return;
      arr[x][y] = "#";
      seen.add(pos);
      if (
        calculatePath(
          arr,
          a[i - 1],
        ) === undefined
      ) {
        obst++;
      }
      arr[x][y] = prev;
    });
    return obst;
  },
  {
    sep: "\n",
    transform: (s) => s.split(""),
  },
);
task.expect(41, 6);

export default task;

Last edited 04. April 2025 13:29