Skip to content

Commit deae5c6

Browse files
committed
feat: 2024 Day 20
1 parent 245e630 commit deae5c6

File tree

4 files changed

+114
-9
lines changed

4 files changed

+114
-9
lines changed

2024/day/20/part/1/solve.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import solve, { findSavedTimeCountsByCheating, parseTrack } from "./solve.ts";
1+
import { findSavedTimeCountsByCheating, parseTrack } from "./solve.ts";
22

33
import { assertEquals } from "@std/assert";
44

2024/day/20/part/1/solve.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
const directions = [[0, -1], [0, 1], [-1, 0], [1, 0]];
22

3-
const factorials = [1, 1, 2];
4-
53
export function parseTrack(text: string) {
64
return text.split("\n").map((line) => [...line]);
75
}
@@ -13,7 +11,6 @@ function buildTimeTrack(track: string[][]) {
1311
const stack = [[xf, yf, 0]];
1412
const seen = new Set<`${number},${number}`>();
1513
while (stack.length) {
16-
// console.log(stack.at(-1));
1714
const [x, y, t] = stack.pop()!;
1815
const hash = `${x},${y}` as const;
1916
if (seen.has(hash)) continue;
@@ -38,18 +35,17 @@ function* manhattanDirections(d: number) {
3835
export function findSavedTimeCountsByCheating(track: string[][]) {
3936
const result = new Map<number, number>();
4037
const timeTrack = buildTimeTrack(track);
38+
const cheatTime = 2;
4139
for (let y = 0; y < timeTrack.length; y++) {
4240
for (let x = 0; x < timeTrack[y].length; x++) {
4341
const t = timeTrack[y][x];
4442
if (t === Infinity) continue;
45-
for (const [dx, dy] of manhattanDirections(2)) {
43+
for (const [dx, dy] of manhattanDirections(cheatTime)) {
4644
const rt = timeTrack[y + dy]?.[x + dx];
4745
if (!isFinite(rt)) continue;
48-
const saved = t - rt - 2;
46+
const saved = t - rt - cheatTime;
4947
if (saved <= 0) continue;
50-
const ax = Math.abs(dx), ay = Math.abs(dy);
51-
const count = factorials[2] / (factorials[ax] * factorials[ay]);
52-
result.set(saved, (result.get(saved) ?? 0) + count);
48+
result.set(saved, (result.get(saved) ?? 0) + 1);
5349
}
5450
}
5551
}

2024/day/20/part/2/solve.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { findSavedTimeCountsByCheating, parseTrack } from "./solve.ts";
2+
3+
import { assertEquals } from "@std/assert";
4+
5+
Deno.test("example", () => {
6+
const input = `\
7+
###############
8+
#...#...#.....#
9+
#.#.#.#.#.###.#
10+
#S#...#.#.#...#
11+
#######.#.#.###
12+
#######.#.#...#
13+
#######.#.###.#
14+
###..E#...#...#
15+
###.#######.###
16+
#...###...#...#
17+
#.#####.#.###.#
18+
#.#...#.#.#...#
19+
#.#.#.#.#.#.###
20+
#...#...#...###
21+
###############`;
22+
23+
const track = parseTrack(input);
24+
25+
assertEquals(
26+
findSavedTimeCountsByCheating(track, { min: 50 }),
27+
new Map([
28+
[50, 32],
29+
[52, 31],
30+
[54, 29],
31+
[56, 39],
32+
[58, 25],
33+
[60, 23],
34+
[62, 20],
35+
[64, 19],
36+
[66, 12],
37+
[68, 14],
38+
[70, 12],
39+
[72, 22],
40+
[74, 4],
41+
[76, 3],
42+
]),
43+
);
44+
});

2024/day/20/part/2/solve.ts

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
const directions = [[0, -1], [0, 1], [-1, 0], [1, 0]];
2+
3+
export function parseTrack(text: string) {
4+
return text.split("\n").map((line) => [...line]);
5+
}
6+
7+
function buildTimeTrack(track: string[][]) {
8+
const result = track.map((row) => row.map(() => Infinity));
9+
const yf = track.findIndex((line) => line.includes("E")),
10+
xf = track[yf].indexOf("E");
11+
const stack = [[xf, yf, 0]];
12+
const seen = new Set<`${number},${number}`>();
13+
while (stack.length) {
14+
const [x, y, t] = stack.pop()!;
15+
const hash = `${x},${y}` as const;
16+
if (seen.has(hash)) continue;
17+
seen.add(hash);
18+
if (track[y][x] === "#") continue;
19+
result[y][x] = t;
20+
for (const [dx, dy] of directions) stack.push([x + dx, y + dy, t + 1]);
21+
}
22+
return result;
23+
}
24+
25+
function* manhattanDirections(d: number) {
26+
for (let offset = 0; offset < d; offset++) {
27+
const complement = d - offset;
28+
yield [offset, complement];
29+
yield [complement, -offset];
30+
yield [-offset, -complement];
31+
yield [-complement, offset];
32+
}
33+
}
34+
35+
export function findSavedTimeCountsByCheating(
36+
track: string[][],
37+
{ min = 1 } = {},
38+
) {
39+
const result = new Map<number, number>();
40+
const timeTrack = buildTimeTrack(track);
41+
for (let cheatTime = 2; cheatTime <= 20; cheatTime++) {
42+
for (let y = 0; y < timeTrack.length; y++) {
43+
for (let x = 0; x < timeTrack[y].length; x++) {
44+
const t = timeTrack[y][x];
45+
if (t === Infinity) continue;
46+
for (const [dx, dy] of manhattanDirections(cheatTime)) {
47+
const rt = timeTrack[y + dy]?.[x + dx];
48+
if (!isFinite(rt)) continue;
49+
const saved = t - rt - cheatTime;
50+
if (saved < min) continue;
51+
result.set(saved, (result.get(saved) ?? 0) + 1);
52+
}
53+
}
54+
}
55+
}
56+
return result;
57+
}
58+
59+
export default function solve(input: string) {
60+
const track = parseTrack(input);
61+
let sum = 0;
62+
const savedTimeCounts = findSavedTimeCountsByCheating(track);
63+
for (const [saved, count] of savedTimeCounts) if (saved >= 100) sum += count;
64+
return sum;
65+
}

0 commit comments

Comments
 (0)