-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPuzzle20.java
127 lines (120 loc) · 4.21 KB
/
Puzzle20.java
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
package advent2024;
import static java.lang.Math.abs;
import adventlib.CharGrid;
import adventlib.CharGrid.Coord;
import adventlib.Dir;
import adventlib.GraphAlgorithms;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.graph.Graph;
import com.google.common.graph.GraphBuilder;
import com.google.common.graph.ImmutableGraph;
import com.google.common.io.CharStreams;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
/**
* @author Éamonn McManus
*/
public class Puzzle20 {
private static final String SAMPLE =
"""
###############
#...#...#.....#
#.#.#.#.#.###.#
#S#...#.#.#...#
#######.#.#.###
#######.#.#...#
#######.#.###.#
###..E#...#...#
###.#######.###
#...###...#...#
#.#####.#.###.#
#.#...#.#.#...#
#.#.#.#.#.#.###
#...#...#...###
###############
""";
private static final Map<String, Callable<Reader>> INPUT_PRODUCERS =
ImmutableMap.of(
"sample",
() -> new StringReader(SAMPLE),
"problem",
() -> new InputStreamReader(Puzzle20.class.getResourceAsStream("puzzle20.txt")));
public static void main(String[] args) throws Exception {
for (var entry : INPUT_PRODUCERS.entrySet()) {
String name = entry.getKey();
try (Reader r = entry.getValue().call()) {
List<String> lines = CharStreams.readLines(r);
var grid = new CharGrid(lines);
Coord start = grid.firstMatch(c -> c == 'S').get();
Coord end = grid.firstMatch(c -> c == 'E').get();
Graph<Coord> graph = makeGraph(grid);
ImmutableList<Coord> path = GraphAlgorithms.shortestPath(graph, start, end);
path = ImmutableList.<Coord>builder().add(start).addAll(path).build();
var distances = GraphAlgorithms.distances(graph, end);
int minSave = 100;
if (name.equals("sample")) {
minSave = 20;
}
System.out.printf(
"For %s, Part 1 cheats saving at least %d: %d\n",
name, minSave, cheatCount(path, distances, 2, minSave));
if (name.equals("sample")) {
minSave = 70;
}
System.out.printf(
"For %s, Part 2 cheats saving at least %d: %d\n",
name, minSave, cheatCount(path, distances, 20, minSave));
}
}
}
// We consider every possible pair (a,b) where a is on the path and b has a Manhattan distance
// from a that is no greater than cheatLength. For Part 1, I initially just considered every
// possible combination of directions for the two moves, but the Part 2 algorithm works just fine
// with cheatLength=2.
private static int cheatCount(
ImmutableList<Coord> path,
ImmutableMap<Coord, Integer> distances,
int cheatLength,
int minSave) {
int cheatCount = 0;
int distance = path.size() - 1;
for (Coord coord : path) {
for (int rowJump = -cheatLength; rowJump <= cheatLength; rowJump++) {
int remain = cheatLength - abs(rowJump);
for (int colJump = -remain; colJump <= remain; colJump++) {
Coord cheat = new Coord(coord.line() + rowJump, coord.col() + colJump);
if (distances.containsKey(cheat)) {
int cheatDistance = distances.get(cheat);
// If the current distance is 80, the cheat distance is 60 then the saving from
// cheating is 80 - 60 - j, where j is the size of the jump.
int saving = distance - cheatDistance - abs(rowJump) - abs(colJump);
if (saving >= minSave) {
cheatCount++;
}
}
}
}
distance--;
}
return cheatCount;
}
private static ImmutableGraph<Coord> makeGraph(CharGrid grid) {
ImmutableGraph.Builder<Coord> builder = GraphBuilder.undirected().immutable();
for (Coord coord : grid.coords()) {
if (grid.get(coord) != '#') {
for (Dir dir : Dir.NEWS) {
Coord moved = dir.move(coord);
if (grid.get(moved) != '#') {
builder.putEdge(coord, moved);
}
}
}
}
return builder.build();
}
}