aboutsummaryrefslogtreecommitdiff
path: root/2024
diff options
context:
space:
mode:
authorThomas Schmucker <ts@its1.de>2024-12-22 17:28:13 +0100
committerThomas Schmucker <ts@its1.de>2024-12-22 17:28:13 +0100
commit8427e039c3a99d1cb7a543a06f9d63897df9f294 (patch)
treeb49f590cdcb91809e248598d9611dc14e9b5bf19 /2024
parent7781a6d76db0a87aecb023b138d77139e44cfc27 (diff)
downloadadvent-of-code-8427e039c3a99d1cb7a543a06f9d63897df9f294.tar.gz
advent-of-code-8427e039c3a99d1cb7a543a06f9d63897df9f294.tar.bz2
advent-of-code-8427e039c3a99d1cb7a543a06f9d63897df9f294.zip
aoc 2024, day 21
Diffstat (limited to '2024')
-rw-r--r--2024/src/day21.cpp193
1 files changed, 193 insertions, 0 deletions
diff --git a/2024/src/day21.cpp b/2024/src/day21.cpp
new file mode 100644
index 0000000..9dd166a
--- /dev/null
+++ b/2024/src/day21.cpp
@@ -0,0 +1,193 @@
1#undef NDEBUG
2#include <algorithm>
3#include <cassert>
4#include <cstdlib>
5#include <fstream>
6#include <iostream>
7#include <map>
8#include <numeric>
9#include <string>
10#include <tuple>
11#include <vector>
12using namespace std;
13
14using pos_type = tuple<int, int>;
15using map_type = map<char, pos_type>;
16
17vector<string>
18read_file(string_view filename)
19{
20 fstream input{ filename };
21 vector<string> lines;
22
23 for ( string line; getline(input, line); ) {
24 lines.emplace_back(line);
25 }
26
27 return lines;
28}
29
30bool
31is_valid_movement(pos_type start, const pos_type dest, string_view movement, const pos_type avoid)
32{
33 static const map_type directions = {
34 { '^', { 0, -1 } },
35 { 'v', { 0, 1 } },
36 { '<', { -1, 0 } },
37 { '>', { 1, 0 } }
38 };
39
40 for ( const auto key: movement ) {
41 const auto [dx, dy] = directions.at(key);
42
43 get<0>(start) += dx;
44 get<1>(start) += dy;
45
46 if ( start == avoid ) {
47 return false;
48 }
49 }
50
51 assert(start == dest); // NOLINT
52
53 return true;
54}
55
56vector<string>
57get_movements(const pos_type start, const pos_type end, const pos_type avoid)
58{
59 if ( start == end ) {
60 return { "A" };
61 }
62
63 const auto [start_x, start_y] = start;
64 const auto [end_x, end_y] = end;
65
66 string movement;
67
68 const auto delta_y = end_y - start_y;
69 if ( delta_y < 0 ) {
70 movement.append(size_t(-delta_y), '^');
71 }
72 else if ( delta_y > 0 ) {
73 movement.append(size_t(delta_y), 'v');
74 }
75
76 const auto delta_x = end_x - start_x;
77 if ( delta_x < 0 ) {
78 movement.append(size_t(-delta_x), '<');
79 }
80 else if ( delta_x > 0 ) {
81 movement.append(size_t(delta_x), '>');
82 }
83
84 assert(!movement.empty()); // NOLINT
85
86 vector<string> movements;
87
88 ranges::sort(movement);
89 do {
90 if ( is_valid_movement(start, end, movement, avoid) ) {
91 movements.emplace_back(movement + 'A');
92 }
93 } while ( next_permutation(movement.begin(), movement.end()) );
94
95 assert(!movements.empty()); // NOLINT
96
97 return movements;
98}
99
100size_t
101find_rec(string_view keys, const int depth, const int max_depth)
102{
103 static const map_type keypad_door{
104 { '7', { 0, 0 } },
105 { '8', { 1, 0 } },
106 { '9', { 2, 0 } },
107
108 { '4', { 0, 1 } },
109 { '5', { 1, 1 } },
110 { '6', { 2, 1 } },
111
112 { '1', { 0, 2 } },
113 { '2', { 1, 2 } },
114 { '3', { 2, 2 } },
115
116 { '0', { 1, 3 } },
117 { 'A', { 2, 3 } },
118 };
119
120 static const map_type keypad_robot{
121 { '^', { 1, 0 } },
122 { 'A', { 2, 0 } },
123
124 { '<', { 0, 1 } },
125 { 'v', { 1, 1 } },
126 { '>', { 2, 1 } }
127 };
128
129 static map<tuple<string, int, int>, size_t> cache;
130
131 if ( cache.contains({ string(keys), depth, max_depth }) ) {
132 return cache.at({ string(keys), depth, max_depth });
133 }
134
135 const auto& keypad = depth == 0 ? keypad_door : keypad_robot;
136 const auto& avoid = depth == 0 ? pos_type{ 0, 3 } : pos_type{ 0, 0 };
137
138 auto pos = keypad.at('A');
139
140 size_t len = 0;
141 for ( const auto key: keys ) {
142 const auto end = keypad.at(key);
143
144 const auto movements = get_movements(pos, end, avoid);
145
146 if ( depth == max_depth ) {
147 len += ranges::min_element(
148 movements, [](const auto& lhs, const auto& rhs) -> bool { return lhs.size() < rhs.size(); })
149 ->size();
150 }
151 else {
152 auto curr_min = numeric_limits<size_t>::max();
153 for ( const auto& movement: movements ) {
154 curr_min = min(curr_min, find_rec(movement, depth + 1, max_depth));
155 }
156 len += curr_min;
157 }
158
159 pos = end;
160 }
161
162 return cache[{ string(keys), depth, max_depth }] = len;
163}
164
165size_t
166find(string_view keys, const int depth)
167{
168 return find_rec(keys, 0, depth);
169}
170
171void
172part1(const vector<string>& lines)
173{
174 cout << accumulate(lines.begin(), lines.end(), 0UL, [](auto init, const auto& line) {
175 return init + find(line, 2) * strtoul(line.c_str(), nullptr, 10);
176 }) << endl;
177}
178
179void
180part2(const vector<string>& lines)
181{
182 cout << accumulate(lines.begin(), lines.end(), 0UL, [](auto init, const auto& line) {
183 return init + find(line, 25) * strtoul(line.c_str(), nullptr, 10);
184 }) << endl;
185}
186
187int
188main()
189{
190 const auto lines = read_file("data/day21.txt");
191 part1(lines);
192 part2(lines);
193}