diff --git a/day20.peggy b/day20.peggy new file mode 100644 index 0000000..3027573 --- /dev/null +++ b/day20.peggy @@ -0,0 +1,2 @@ +lines = (@[#.SE]+ "\n")+ + diff --git a/day20.ts b/day20.ts new file mode 100644 index 0000000..0352e79 --- /dev/null +++ b/day20.ts @@ -0,0 +1,109 @@ +import { PointSet } from './lib/rect.ts'; +import { AllDirs, Point, PointMap, Rect } from './lib/rect.ts'; +import { type MainArgs, parseFile } from './lib/utils.ts'; +import { BinaryHeap } from '@std/data-structures'; + +type Parsed = string[][]; + +function astar(r: Rect, start: Point, end: Point): PointSet { + const gScore = new PointMap([[start, 0]]); + const fScore = new PointMap([[start, 0]]); + const prev = new PointMap(); + const backlog = new BinaryHeap( + (a, b) => fScore.get(a)! - fScore.get(b)!, + ); + backlog.push(start); + + while (!backlog.isEmpty()) { + const p = backlog.pop()!; + const g = gScore.get(p)!; + + if (p.equals(end)) { + const ps = new PointSet(); + let n: Point | undefined = end; + while (n) { + ps.add(n); + n = prev.get(n); + } + return ps; + } + + for (const d of AllDirs) { + const next = p.inDir(d); + if (r.check(next) && (r.get(next) !== '#')) { + const nextG = gScore.get(next) ?? Infinity; + if (g + 1 < nextG) { + gScore.set(next, g + 1); + fScore.set(next, g + next.manhattan(end)); + backlog.push(next); + prev.set(next, p); + } + } + } + } + + return new PointSet(); +} + +function part1(inp: Parsed): number { + const r = new Rect(inp); + const [start] = r.filter((v) => v === 'S'); + const [end] = r.filter((v) => v === 'E'); + + const ps = astar(r, start, end); + const pss = ps.size; + const target = pss - 100; + let tot = 0; + let pcount = 0; + for (const p of ps) { + let qcount = 0; + for (const q of ps) { + if (p.equals(q)) { + continue; + } + const d = p.manhattan(q); + if (d === 2) { + if ((pcount + (pss - qcount) + 1) <= target) { + tot++; + } + } + qcount++; + } + pcount++; + } + return tot; +} + +function part2(inp: Parsed): number { + const r = new Rect(inp); + const [start] = r.filter((v) => v === 'S'); + const [end] = r.filter((v) => v === 'E'); + + const ps = astar(r, start, end); + const pss = ps.size; + const target = pss - 100; + let tot = 0; + let pcount = 0; + for (const p of ps) { + let qcount = 0; + for (const q of ps) { + if (p.equals(q)) { + continue; + } + const d = p.manhattan(q); + if (d <= 20) { + if ((pcount + (pss - qcount) + d - 1) <= target) { + tot++; + } + } + qcount++; + } + pcount++; + } + return tot; +} + +export default async function main(args: MainArgs): Promise<[number, number]> { + const inp = await parseFile(args); + return [part1(inp), part2(inp)]; +} diff --git a/inputs b/inputs index 6c95653..10381f6 160000 --- a/inputs +++ b/inputs @@ -1 +1 @@ -Subproject commit 6c956531600c1f1c6de53b7abc4b6d4d435b9456 +Subproject commit 10381f66c4eab5d2ca65db31bc0378c61d6c8047 diff --git a/test/day20.js b/test/day20.js new file mode 100644 index 0000000..9906db5 --- /dev/null +++ b/test/day20.js @@ -0,0 +1 @@ +export default [1429, 988931];