Skip to content

Commit 4c0a8fe

Browse files
committed
2024/20
1 parent 29de4d0 commit 4c0a8fe

File tree

2 files changed

+37
-28
lines changed

2 files changed

+37
-28
lines changed

2024/Day20/README.md

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
## --- Day 20: Race Condition ---
2-
The Historians are quite pixelated again. This time, a massive, black building looms over you - you're [right outside](/2017/day/24) the CPU!
2+
The Historians are quite pixelated again. This time, a massive, black building looms over you - you're _right outside_ the CPU!
33

44
While The Historians get to work, a nearby program sees that you're idle and challenges you to a <em>race</em>. Apparently, you've arrived just in time for the frequently-held <em>race condition</em> festival!
55

66
_Visit the website for the full story and [full puzzle](https://adventofcode.com/2024/day/20) description._
7+
8+
Here is your corrected text with improved grammar and flow:
9+
10+
The problem included a small but crucial hint: _there is only a single path from the start to the end_. Moreover, there are no dead ends in the input; it's just a single, continuous trace.
11+
12+
I created a function that returns the points of the track in from finish to start. This way, the index of an item in the array corresponds to its distance to the finish line. Then, I go over the path. For each position, the number of possible cheats is calculated by checking what happens if we are trying to make a shortcut to any other positions that is closer to the finish line.
13+
14+
There are a number of cases to consider:
15+
- the target position is too far away. This happens when its Manhattan distance is greater than the allowed _cheat_ limit
16+
- the target is within range, but the saving is less than 100
17+
- the target is within range, and the saving is at least 100
18+
19+
We need to determine the number of good cheats for each position add them up. I used Parallel LINQ here, as the regular sequential one took significantly more time.

2024/Day20/Solution.cs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,29 +13,26 @@ class Solution : Solver {
1313

1414
int Solve(string input, int cheat) {
1515
var path = GetPath(input);
16-
17-
// this nested loop is 6x times faster then the same thing with LINQ ¯\_(ツ)_/¯
18-
var res = 0;
19-
for (var i = 0; i < path.Length; i++) {
20-
for (var j = i + 1; j < path.Length; j++) {
21-
var dist = Manhattan(path[i], path[j]);
22-
23-
// the index of an element in the path equals to its distance
24-
// from the finish line
25-
26-
var saving = j - (i + dist);
27-
if (dist <= cheat && saving >= 100) {
28-
res++;
29-
}
30-
}
31-
}
32-
return res;
16+
var indices = Enumerable.Range(0, path.Length).ToArray();
17+
18+
// sum up the worthy cheats for each index along the path
19+
var cheatsFromI = (int i) => (
20+
from j in indices[0..i]
21+
let dist = Manhattan(path[i], path[j])
22+
let saving = i - (j + dist)
23+
where dist <= cheat && saving >= 100
24+
select 1
25+
).Sum();
26+
27+
// parallel is gold today, it gives us an 3-4x boost
28+
return indices.AsParallel().Select(cheatsFromI).Sum();
3329
}
3430

3531
int Manhattan(Complex a, Complex b) =>
3632
(int)(Math.Abs(a.Imaginary - b.Imaginary) + Math.Abs(a.Real - b.Real));
3733

38-
// follow the path from start to finish, supposed that there is a single track in the input
34+
// follow the path from finish to start, supposed that there is a single track in the input
35+
// the index of a position in the returned array equals to its distance from the finish
3936
Complex[] GetPath(string input) {
4037
var lines = input.Split("\n");
4138
var map = (
@@ -44,19 +41,18 @@ from x in Enumerable.Range(0, lines[0].Length)
4441
select new KeyValuePair<Complex, char>(x + y * Complex.ImaginaryOne, lines[y][x])
4542
).ToDictionary();
4643

44+
Complex[] dirs = [-1, 1, Complex.ImaginaryOne, -Complex.ImaginaryOne];
45+
4746
var start = map.Keys.Single(k => map[k] == 'S');
4847
var goal = map.Keys.Single(k => map[k] == 'E');
4948

50-
var (prev, cur, dir) = ((Complex?)null, start, Complex.ImaginaryOne);
49+
var (prev, cur) = ((Complex?)null, goal);
50+
var res = new List<Complex> { cur };
5151

52-
var res = new List<Complex> { start };
53-
while (cur != goal) {
54-
if (map[cur + dir] == '#' || cur + dir == prev) {
55-
dir *= Complex.ImaginaryOne;
56-
} else {
57-
(prev, cur) = (cur, cur + dir);
58-
res.Add(cur);
59-
}
52+
while (cur != start) {
53+
var dir = dirs.Single(dir => map[cur + dir] != '#' && cur + dir != prev);
54+
(prev, cur) = (cur, cur + dir);
55+
res.Add(cur);
6056
}
6157
return res.ToArray();
6258
}

0 commit comments

Comments
 (0)