aboutsummaryrefslogtreecommitdiff
path: root/2024/src
diff options
context:
space:
mode:
Diffstat (limited to '2024/src')
-rw-r--r--2024/src/day16.cpp195
1 files changed, 195 insertions, 0 deletions
diff --git a/2024/src/day16.cpp b/2024/src/day16.cpp
new file mode 100644
index 0000000..9e015c2
--- /dev/null
+++ b/2024/src/day16.cpp
@@ -0,0 +1,195 @@
1#include <fstream>
2#include <iostream>
3#include <map>
4#include <optional>
5#include <queue>
6#include <set>
7#include <string>
8#include <tuple>
9using namespace std;
10
11using pos_type = tuple<size_t, size_t>;
12using walls_type = set<pos_type>;
13
14// pos, direction, score
15using index_type = tuple<pos_type, size_t, int>;
16
17struct index_compare {
18 bool operator()(const index_type& lhs, const index_type& rhs)
19 {
20 // compare score
21 return get<2>(lhs) > get<2>(rhs);
22 }
23};
24
25using index_pq = priority_queue<index_type, vector<index_type>, index_compare>;
26
27static const array<pos_type, 4> dirs = {
28 pos_type{ -1, 0 },
29 pos_type{ 0, 1 },
30 pos_type{ 1, 0 },
31 pos_type{ 0, -1 }
32};
33
34tuple<walls_type, pos_type, pos_type>
35read_file(string_view filename)
36{
37 fstream input{ filename };
38 walls_type walls;
39 pos_type start;
40 pos_type finish;
41
42 size_t yPos = 0;
43 for ( string line; getline(input, line); ) {
44 for ( size_t xPos = 0; xPos != line.size(); ++xPos ) {
45 auto chr = line.at(xPos);
46 if ( chr == '#' ) {
47 walls.emplace(xPos, yPos);
48 }
49 else if ( chr == 'S' ) {
50 start = { xPos, yPos };
51 }
52 else if ( chr == 'E' ) {
53 finish = { xPos, yPos };
54 }
55 }
56 ++yPos;
57 }
58
59 return { walls, start, finish };
60}
61
62void
63print(const walls_type& walls, pos_type start, pos_type finish)
64{
65 size_t width = 0;
66 size_t height = 0;
67
68 for ( const auto& [x, y]: walls ) {
69 width = max(width, x + 1);
70 height = max(height, y + 1);
71 }
72
73 for ( size_t yPos = 0; yPos != height; ++yPos ) {
74 for ( size_t xPos = 0; xPos != width; ++xPos ) {
75 const pos_type pos{ xPos, yPos };
76 if ( pos == start ) {
77 cout << 'S';
78 }
79 else if ( pos == finish ) {
80 cout << 'E';
81 }
82 else if ( walls.contains(pos) ) {
83 cout << '#';
84 }
85 else {
86 cout << '.';
87 }
88 }
89 cout << '\n';
90 }
91 cout << endl;
92}
93
94int
95part1(const tuple<walls_type, pos_type, pos_type>& data)
96{
97 const auto& [walls, start, finish] = data;
98
99 // pos, direction
100 set<tuple<pos_type, size_t>> seen;
101
102 index_pq queue;
103 queue.push({ start, 2, 0 });
104
105 while ( !queue.empty() ) {
106 const auto [pos, dir, score] = queue.top();
107 queue.pop();
108
109 if ( pos == finish ) {
110 return score;
111 }
112
113 if ( seen.contains({ pos, dir }) ) {
114 continue;
115 }
116 seen.insert({ pos, dir });
117
118 const auto delta = dirs.at(dir);
119 const pos_type next = { get<0>(pos) + get<0>(delta),
120 get<1>(pos) + get<1>(delta) };
121
122 if ( !walls.contains(next) ) {
123 queue.push({ next, dir, score + 1 });
124 }
125 queue.push({ pos, (dir + 1) % 4, score + 1000 });
126 queue.push({ pos, (dir + 3) % 4, score + 1000 });
127 }
128 return -1;
129}
130
131map<tuple<pos_type, size_t>, int>
132walk(const tuple<walls_type, pos_type, pos_type>& data, const vector<index_type>& starts, size_t offset)
133{
134 const auto& [walls, start, finish] = data;
135
136 set<tuple<pos_type, size_t>> seen;
137
138 map<tuple<pos_type, size_t>, int> scores;
139
140 index_pq queue{ starts.begin(), starts.end() };
141
142 while ( !queue.empty() ) {
143 const auto [pos, dir, score] = queue.top();
144 queue.pop();
145
146 if ( !scores.contains({ pos, dir }) ) {
147 scores[{ pos, dir }] = score;
148 }
149
150 if ( seen.contains({ pos, dir }) ) {
151 continue;
152 }
153 seen.insert({ pos, dir });
154
155 const auto delta = dirs.at((dir + offset) % 4);
156 const pos_type next = { get<0>(pos) + get<0>(delta),
157 get<1>(pos) + get<1>(delta) };
158
159 if ( !walls.contains(next) ) {
160 queue.push({ next, dir, score + 1 });
161 }
162 queue.push({ pos, (dir + 1) % 4, score + 1000 });
163 queue.push({ pos, (dir + 3) % 4, score + 1000 });
164 }
165 return scores;
166}
167
168size_t
169part2(const tuple<walls_type, pos_type, pos_type>& data, int best)
170{
171 const auto& [walls, start, finish] = data;
172
173 auto scores_forwards = walk({ walls, start, finish }, { { start, 2, 0 } }, 0);
174 auto scores_backwards = walk({ walls, finish, start }, { { finish, 0, 0 }, { finish, 1, 0 }, { finish, 2, 0 }, { finish, 3, 0 } }, 2);
175
176 set<pos_type> match;
177 for ( const auto& [pos_dir, score]: scores_forwards ) {
178 if ( scores_backwards.contains(pos_dir) && scores_backwards.at(pos_dir) + score == best ) {
179 const auto [pos, dir] = pos_dir;
180 match.insert(pos);
181 }
182 }
183 return match.size();
184}
185
186int
187main()
188{
189 auto data = read_file("data/day16.txt");
190
191 auto score = part1(data);
192 cout << score << endl;
193
194 cout << part2(data, score) << endl;
195}