Skip to content

Commit e5f96d3

Browse files
committed
aoc 2024 day 20
1 parent 996c7fe commit e5f96d3

File tree

12 files changed

+340
-17
lines changed

12 files changed

+340
-17
lines changed

adventofcode/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,7 +1454,7 @@
14541454
[20232402tests]: src/test/java/org/ck/adventofcode/year2023/Day24Test.java
14551455
[20232501tests]: src/test/java/org/ck/adventofcode/year2023/Day25Test.java
14561456

1457-
# 2024 (38/49)
1457+
# 2024 (40/49)
14581458

14591459
| # | Name | Solution | Test |
14601460
|---------:|-----------------------------------------------------|:------------------------------------:|:---------------------------------:|
@@ -1496,8 +1496,8 @@
14961496
| 20241802 | [Day 18: RAM Run - Part 2][20241802] | ✅[💾][20241802solution] | ✅[💾][20241802tests] |
14971497
| 20241901 | [Day 19: Linen Layout][20241901] | ✅[💾][20241901solution] | ✅[💾][20241901tests] |
14981498
| 20241902 | [Day 19: Linen Layout - Part 2][20241902] | ✅[💾][20241902solution] | ✅[💾][20241902tests] |
1499-
| 20242001 | [Day 20: ?][20242001] | [💾][20242001solution] | [💾][20242001tests] |
1500-
| 20242002 | [Day 20: ? - Part 2][20242002] | [💾][20242002solution] | [💾][20242002tests] |
1499+
| 20242001 | [Day 20: Race Condition][20242001] | ✅[💾][20242001solution] | ✅[💾][20242001tests] |
1500+
| 20242002 | [Day 20: Race Condition - Part 2][20242002] | ✅[💾][20242002solution] | ✅[💾][20242002tests] |
15011501
| 20242101 | [Day 21: ?][20242101] | [💾][20242101solution] | [💾][20242101tests] |
15021502
| 20242102 | [Day 21: ? - Part 2][20242102] | [💾][20242102solution] | [💾][20242102tests] |
15031503
| 20242201 | [Day 22: ?][20242201] | [💾][20242201solution] | [💾][20242201tests] |
Lines changed: 155 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,178 @@
11
package org.ck.adventofcode.year2024;
22

3-
import java.util.Scanner;
3+
import java.util.*;
44
import org.ck.adventofcode.util.AOCSolution;
55
import org.ck.codechallengelib.annotation.Solution;
66

77
@Solution(
88
id = 20242001,
9-
name = "Day 20: ?",
9+
name = "Day 20: Race Condition",
1010
url = "https://adventofcode.com/2024/day/20",
11-
category = "2024",
12-
solved = false)
11+
category = "2024")
1312
@Solution(
1413
id = 20242002,
15-
name = "Day 20: ? - Part 2",
14+
name = "Day 20: Race Condition - Part 2",
1615
url = "https://adventofcode.com/2024/day/20#part2",
17-
category = "2024",
18-
solved = false)
16+
category = "2024")
1917
public class Day20 extends AOCSolution {
2018

2119
@Override
2220
protected void runPartOne(final Scanner in) {
23-
run(in);
21+
run(in, 2);
2422
}
2523

2624
@Override
2725
protected void runPartTwo(final Scanner in) {
28-
run(in);
26+
run(in, 20);
2927
}
3028

31-
private void run(final Scanner in) {
32-
print("Whoopsie");
29+
private void run(final Scanner in, final int allowedCheatTime) {
30+
final int wantedSaveAmount = Integer.parseInt(in.nextLine());
31+
32+
final List<String> grid = new ArrayList<>();
33+
Coordinate start = null;
34+
Coordinate end = null;
35+
36+
int row = 0;
37+
while (in.hasNextLine()) {
38+
final String line = in.nextLine();
39+
40+
if (line.contains("S")) {
41+
start = new Coordinate(line.indexOf('S'), row);
42+
}
43+
if (line.contains("E")) {
44+
end = new Coordinate(line.indexOf('E'), row);
45+
}
46+
47+
grid.add(line);
48+
++row;
49+
}
50+
51+
if (start == null || end == null) {
52+
throw new IllegalArgumentException("Start and end coordinates must be provided");
53+
}
54+
55+
final long[][] distances = new long[grid.size()][grid.getFirst().length()];
56+
for (final long[] distanceRow : distances) {
57+
Arrays.fill(distanceRow, Long.MAX_VALUE);
58+
}
59+
60+
fillFromStartToEnd(distances, grid, start, end);
61+
62+
fillReachableFromEnd(distances, grid, end);
63+
64+
final Map<Long, Long> savesPerAmount = getSavesPerAmount(distances, allowedCheatTime);
65+
66+
long savesOverValue = 0;
67+
for (final Map.Entry<Long, Long> entry : savesPerAmount.entrySet()) {
68+
if (entry.getKey() >= wantedSaveAmount) {
69+
savesOverValue += entry.getValue();
70+
}
71+
}
72+
73+
print(savesOverValue);
74+
}
75+
76+
private static void fillFromStartToEnd(
77+
final long[][] distances,
78+
final List<String> grid,
79+
final Coordinate start,
80+
final Coordinate end) {
81+
final Queue<State> queue = new PriorityQueue<>(Comparator.comparingLong(State::steps));
82+
queue.add(new State(start, 0));
83+
84+
while (!queue.isEmpty()) {
85+
final State state = queue.poll();
86+
final Coordinate coordinate = state.coordinate();
87+
final long steps = state.steps();
88+
89+
distances[coordinate.y()][coordinate.x()] = steps;
90+
91+
if (coordinate.equals(end)) {
92+
break;
93+
}
94+
95+
final long nextSteps = steps + 1;
96+
visitNeighbours(distances, grid, coordinate, nextSteps, queue);
97+
}
3398
}
99+
100+
private static void fillReachableFromEnd(
101+
final long[][] distances, final List<String> grid, final Coordinate end) {
102+
final Queue<State> queue = new PriorityQueue<>(Comparator.comparingLong(State::steps));
103+
queue.add(new State(end, distances[end.y()][end.x()]));
104+
105+
while (!queue.isEmpty()) {
106+
final State state = queue.poll();
107+
final Coordinate coordinate = state.coordinate();
108+
final long steps = state.steps();
109+
110+
distances[coordinate.y()][coordinate.x()] = steps;
111+
112+
final long nextSteps = steps - 1;
113+
visitNeighbours(distances, grid, coordinate, nextSteps, queue);
114+
}
115+
}
116+
117+
private static void visitNeighbours(
118+
final long[][] distances,
119+
final List<String> grid,
120+
final Coordinate coordinate,
121+
final long nextSteps,
122+
final Queue<State> queue) {
123+
for (final Coordinate next :
124+
Set.of(
125+
new Coordinate(coordinate.x() - 1, coordinate.y()),
126+
new Coordinate(coordinate.x() + 1, coordinate.y()),
127+
new Coordinate(coordinate.x(), coordinate.y() - 1),
128+
new Coordinate(coordinate.x(), coordinate.y() + 1))) {
129+
if (grid.get(next.y).charAt(next.x()) != '#' && distances[next.y()][next.x()] > nextSteps) {
130+
queue.add(new State(next, nextSteps));
131+
}
132+
}
133+
}
134+
135+
private static Map<Long, Long> getSavesPerAmount(
136+
final long[][] distances, final int allowedCheatTime) {
137+
final Map<Long, Long> savesPerAmount = new HashMap<>();
138+
139+
for (int y1 = 1; y1 < distances.length - 1; ++y1) {
140+
for (int x1 = 1; x1 < distances[y1].length - 1; ++x1) {
141+
final long distance1 = distances[y1][x1];
142+
143+
if (distance1 < Long.MAX_VALUE) {
144+
getCheatsPerStart(distances, allowedCheatTime, y1, x1, distance1, savesPerAmount);
145+
}
146+
}
147+
}
148+
return savesPerAmount;
149+
}
150+
151+
private static void getCheatsPerStart(
152+
final long[][] distances,
153+
final int allowedCheatTime,
154+
final int y1,
155+
final int x1,
156+
final long distance1,
157+
final Map<Long, Long> savesPerAmount) {
158+
for (int y2 = 1; y2 < distances.length - 1; ++y2) {
159+
for (int x2 = 1; x2 < distances[y1].length - 1; ++x2) {
160+
final long distance2 = distances[y2][x2];
161+
final long cheatTime = (long) Math.abs(y1 - y2) + Math.abs(x1 - x2);
162+
163+
if (distance2 < Long.MAX_VALUE && distance1 < distance2 && cheatTime <= allowedCheatTime) {
164+
final long saveAmount = distance2 - distance1 - cheatTime;
165+
166+
if (saveAmount > 0) {
167+
savesPerAmount.put(
168+
saveAmount, savesPerAmount.computeIfAbsent(saveAmount, key -> 0L) + 1);
169+
}
170+
}
171+
}
172+
}
173+
}
174+
175+
private record Coordinate(int x, int y) {}
176+
177+
private record State(Coordinate coordinate, long steps) {}
34178
}

0 commit comments

Comments
 (0)