-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathday16.ts
128 lines (114 loc) · 2.96 KB
/
day16.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import { AllDirs, Dir, Point, Rect } from './lib/rect.ts';
import { type MainArgs, mod, parseFile } from './lib/utils.ts';
import { BinaryHeap } from '@std/data-structures';
import { PointSet } from './lib/rect.ts';
type Parsed = string[][];
function toId(p: Point, d: Dir): string {
return `${p}-${d}`;
}
interface PointNode {
id: string;
pos: Point;
dir: Dir;
gScore: number;
fScore: number;
prev: Set<string>;
}
interface AstarResults {
start: Point;
end: Point;
minScore: number;
points: Map<string, PointNode>;
}
function astar(inp: Parsed): AstarResults {
const r = new Rect(inp);
const start = r.indexOf('S')!;
const end = r.indexOf('E')!;
const sn: PointNode = {
id: toId(start, Dir.E),
pos: start,
dir: Dir.E,
gScore: 0,
fScore: 0,
prev: new Set(),
};
const points = new Map<string, PointNode>([[sn.id, sn]]);
const backlog = new BinaryHeap<PointNode>(
(a, b) => a.fScore - b.fScore,
);
backlog.push(sn);
function check(pos: Point, dir: Dir, newScore: number, prevId: string): void {
const id = toId(pos, dir);
let node = points.get(id);
if (!node) {
node = {
id: toId(pos, dir),
pos,
dir,
gScore: newScore,
fScore: newScore + pos.manhattan(end),
prev: new Set([prevId]),
};
points.set(id, node);
backlog.push(node);
} else if (newScore < node.gScore) {
node.gScore = newScore;
node.fScore = newScore + pos.manhattan(end);
node.prev = new Set([prevId]);
backlog.push(node);
} else if (newScore === node.gScore) {
// Track all paths that got us to this score, for reconstruction.
node.prev.add(prevId);
}
}
let minScore = Infinity;
while (!backlog.isEmpty()) {
const n = backlog.pop()!;
const { id, pos, dir, gScore } = n;
if (pos.equals(end)) {
minScore = Math.min(gScore, minScore);
continue;
}
if (gScore > minScore) {
continue;
}
const f = pos.inDir(dir);
if (r.check(f) && r.get(f) !== '#') {
check(f, dir, gScore + 1, id);
}
check(pos, mod(dir - 1, 4), gScore + 1000, id);
check(pos, mod(dir + 1, 4), gScore + 1000, id);
}
return {
start,
end,
minScore,
points,
};
}
function part1(res: AstarResults): number {
return res.minScore;
}
function part2(res: AstarResults): number {
const { points, end } = res;
const seen = new PointSet();
const reconstruct: string[] = [];
for (const d of AllDirs) {
const id = toId(end, d);
if (isFinite(points.get(id)?.gScore ?? Infinity)) {
reconstruct.push(id);
}
}
while (reconstruct.length) {
const id = reconstruct.shift()!;
const { pos, prev } = points.get(id)!;
seen.add(pos);
reconstruct.push(...prev);
}
return seen.size;
}
export default async function main(args: MainArgs): Promise<[number, number]> {
const inp = await parseFile<Parsed>(args);
const res = astar(inp);
return [part1(res), part2(res)];
}