Skip to content

Commit 81d4f91

Browse files
committed
Add solutions to day 21
1 parent b90ca6f commit 81d4f91

File tree

4 files changed

+154
-0
lines changed

4 files changed

+154
-0
lines changed

src/day21/day21.test.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import * as fs from "node:fs/promises";
2+
import { suite, test } from "node:test";
3+
4+
import { part1, part2 } from "./day21.ts";
5+
6+
const [INPUT, INPUT_TEST] = await Promise.all([
7+
fs.readFile(new URL("input.txt", import.meta.url), "utf-8"),
8+
fs.readFile(new URL("input-test.txt", import.meta.url), "utf-8"),
9+
]);
10+
11+
suite("day21", () => {
12+
test("part1", (t) => {
13+
t.assert.equal(part1(INPUT_TEST), 126_384);
14+
t.assert.equal(part1(INPUT), 134_120);
15+
});
16+
17+
test("part2", (t) => {
18+
t.assert.equal(part2(INPUT_TEST), 154_115_708_116_294);
19+
t.assert.equal(part2(INPUT), 167_389_793_580_400);
20+
});
21+
});

src/day21/day21.ts

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { lines, memoize } from "../utils.ts";
2+
3+
const GRID = "789456123 0A ^@<v>";
4+
const GRID_WIDTH = 3;
5+
const GRID_HEIGHT = GRID.length / GRID_WIDTH;
6+
7+
const DIRECTIONS = [
8+
[">", 1, 0],
9+
["v", 0, 1],
10+
["<", -1, 0],
11+
["^", 0, -1],
12+
] as const;
13+
14+
const uniquePathsBetween = memoize(
15+
(startTile, endTile) => `${startTile}${endTile}`,
16+
(startTile: string, endTile: string): readonly string[] => {
17+
const startPos = GRID.indexOf(startTile);
18+
const endPos = GRID.indexOf(endTile);
19+
20+
if (startPos === -1 || endPos === -1) {
21+
throw new RangeError("No tile pos found!");
22+
}
23+
24+
const queue = [{ pos: startPos, seq: "" }];
25+
const costs = new Map<number, number>();
26+
const sequences = [];
27+
28+
let next;
29+
while ((next = queue.shift()) !== undefined) {
30+
const { pos, seq } = next;
31+
32+
if (pos === endPos) {
33+
sequences.push(seq);
34+
continue;
35+
}
36+
37+
const x = pos % GRID_WIDTH;
38+
const y = (pos - x) / GRID_WIDTH;
39+
40+
for (const [dir, dx, dy] of DIRECTIONS) {
41+
const nextX = x + dx;
42+
const nextY = y + dy;
43+
44+
if (
45+
0 <= nextX &&
46+
nextX < GRID_WIDTH &&
47+
0 <= nextY &&
48+
nextY < GRID_HEIGHT
49+
) {
50+
const nextPos = GRID_WIDTH * nextY + nextX;
51+
const nextSeq = seq + dir;
52+
const nextCost = nextSeq.length;
53+
const prevBestCost = costs.get(nextPos);
54+
if (
55+
GRID[nextPos] !== " " &&
56+
(prevBestCost === undefined || nextCost <= prevBestCost)
57+
) {
58+
costs.set(nextPos, nextCost);
59+
queue.push({ pos: nextPos, seq: seq.concat(dir) });
60+
}
61+
}
62+
}
63+
}
64+
65+
return sequences;
66+
},
67+
);
68+
69+
const shortestSequenceLength = memoize(
70+
(sequence, depth, limit) => `${sequence},${depth},${limit}`,
71+
(sequence: string, depth: number, limit: number): number => {
72+
if (depth > limit) {
73+
return sequence.length;
74+
}
75+
76+
let sequenceLength = 0;
77+
78+
let prevTile = depth === 0 ? "A" : "@";
79+
80+
for (let nextTile of sequence) {
81+
if (0 < depth && nextTile === "A") {
82+
nextTile = "@";
83+
}
84+
85+
let shortestSubSequenceLength = Infinity;
86+
87+
for (const nextPath of uniquePathsBetween(prevTile, nextTile)) {
88+
shortestSubSequenceLength = Math.min(
89+
shortestSubSequenceLength,
90+
shortestSequenceLength(nextPath + "@", depth + 1, limit),
91+
);
92+
}
93+
94+
sequenceLength += shortestSubSequenceLength;
95+
96+
prevTile = nextTile;
97+
}
98+
99+
return sequenceLength;
100+
},
101+
);
102+
103+
function solve(input: string, limit: number): number {
104+
const codes = lines(input);
105+
106+
let sum = 0;
107+
108+
for (const code of codes) {
109+
const length = shortestSequenceLength(code, 0, limit);
110+
const numeric = Number.parseInt(code.slice(0, -1));
111+
sum += length * numeric;
112+
}
113+
114+
return sum;
115+
}
116+
117+
export function part1(input: string): number {
118+
return solve(input, 2);
119+
}
120+
121+
export function part2(input: string): number {
122+
return solve(input, 25);
123+
}

src/day21/input-test.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
029A
2+
980A
3+
179A
4+
456A
5+
379A

src/day21/input.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
789A
2+
540A
3+
285A
4+
140A
5+
189A

0 commit comments

Comments
 (0)