1
+ #include < iostream>
2
+ #include < filesystem>
3
+ #include < fstream>
4
+ #include < string>
5
+ #include < vector>
6
+ #include < benchmark/benchmark.h>
7
+ #include < format>
8
+ #include < aoc/2024/pos.h>
9
+ #include < aoc/2024/split.h>
10
+ #include < map>
11
+ #include < set>
12
+
13
+ struct Data {
14
+ std::vector<Pos> positions;
15
+ Pos start;
16
+ Pos end;
17
+ };
18
+
19
+ bool in_bounds (const Pos &pos, int width, int height)
20
+ {
21
+ return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height;
22
+ }
23
+
24
+ struct State {
25
+ Pos pos;
26
+ Pos dir;
27
+ bool operator <(const State& other) const {
28
+ return std::tie (pos.x , pos.y , dir.x , dir.y ) < std::tie (other.pos .x , other.pos .y , other.dir .x , other.dir .y );
29
+ }
30
+ };
31
+
32
+ std::pair<int64_t , int64_t > djikstra (const Data& data, int64_t width, int64_t height, int64_t limit)
33
+ {
34
+ std::set<Pos> occupied;
35
+ for (size_t i = 0 ; i < limit; ++i) {
36
+ occupied.insert (data.positions [i]);
37
+ }
38
+ std::priority_queue<std::pair<int64_t , State>, std::vector<std::pair<int64_t , State>>, std::greater<>> pq;
39
+ std::map<State, int64_t > dist;
40
+ std::map<State, std::vector<State>> prev;
41
+ std::set<State> visited;
42
+
43
+ State start = State{.pos = data.start , .dir = E};
44
+ pq.emplace (0 , start);
45
+ dist[start] = 0 ;
46
+ State end_state;
47
+
48
+ int64_t distance = 0 ;
49
+ while (!pq.empty ()) {
50
+ auto elem = pq.top ();
51
+ pq.pop ();
52
+ int64_t cur_dist = elem.first ;
53
+ State u = elem.second ;
54
+ visited.insert (u);
55
+
56
+ if (u.pos == data.end ) {
57
+ end_state = u;
58
+ distance = cur_dist;
59
+ break ;
60
+ }
61
+
62
+ for (auto & new_dir : directions) {
63
+ State v = {.pos = u.pos + new_dir, .dir = new_dir};
64
+
65
+ if (!in_bounds (v.pos , width, height) || visited.contains (v) || occupied.contains (v.pos )) {
66
+ continue ;
67
+ }
68
+
69
+ int64_t new_dist = cur_dist + 1 ;
70
+ if (!dist.contains (v) || new_dist < dist[v]) {
71
+ dist[v] = new_dist;
72
+ prev[v] = {u};
73
+ pq.emplace (new_dist, v);
74
+ }
75
+ else if (new_dist == dist[v]) {
76
+ prev[v].push_back (u);
77
+ }
78
+ }
79
+ }
80
+
81
+
82
+ std::vector<std::string> grid (height, std::string (width, ' .' ));
83
+
84
+ std::vector<State> stack;
85
+ stack.push_back (end_state);
86
+ std::set<Pos> visited_pos;
87
+ while (stack.size () != 0 && stack[0 ].pos != data.start ) {
88
+ visited_pos.insert (stack.back ().pos );
89
+ auto cur = stack.back ();
90
+ grid[cur.pos .y ][cur.pos .x ] = ' O' ;
91
+ stack.pop_back ();
92
+ stack.push_back (prev[cur][0 ]);
93
+ // for(auto& prev_state : prev[cur]) {
94
+ // stack.push_back(prev_state);
95
+ // }
96
+ }
97
+ grid[data.start .y ][data.start .x ] = ' O' ;
98
+
99
+ for (size_t i = 0 ; i < limit; ++i) {
100
+ grid[data.positions [i].y ][data.positions [i].x ] = ' #' ;
101
+ }
102
+
103
+ for (auto & row : grid) {
104
+ std::cout << row << std::endl;
105
+ }
106
+ std::cout << std::endl;
107
+
108
+ return {distance, visited_pos.size ()};
109
+ }
110
+
111
+ int part1 (const Data &data)
112
+ {
113
+ auto copy = data;
114
+ int64_t width = 71 ;
115
+ int64_t height = 71 ;
116
+ copy.start = Pos{.x = 0 , .y = 0 };
117
+ copy.end = Pos{.x = width - 1 , .y = height - 1 };
118
+ auto [distance, visited] = djikstra (copy, width, height, 1024 );
119
+ return distance;
120
+ }
121
+
122
+ int part2 (const Data &data)
123
+ {
124
+ return 0 ;
125
+ }
126
+
127
+ Data parse ()
128
+ {
129
+ std::ifstream file (std::filesystem::path (" inputs/day18.txt" ));
130
+ if (!file.is_open ())
131
+ {
132
+ throw std::runtime_error (" file not found" );
133
+ }
134
+ std::string line;
135
+ Data data;
136
+
137
+ while (std::getline (file, line))
138
+ {
139
+ auto location = split_to_int (line, " ," );
140
+ data.positions .push_back (Pos{.x = location[0 ], .y = location[1 ]});
141
+ }
142
+
143
+ return data;
144
+ }
145
+
146
+ class BenchmarkFixture : public benchmark ::Fixture
147
+ {
148
+ public:
149
+ static Data data;
150
+ };
151
+
152
+ Data BenchmarkFixture::data = parse();
153
+
154
+ BENCHMARK_DEFINE_F (BenchmarkFixture, Part1Benchmark)
155
+ (benchmark::State &state)
156
+ {
157
+ for (auto _ : state)
158
+ {
159
+ auto s = part1 (data);
160
+ benchmark::DoNotOptimize (s);
161
+ }
162
+ }
163
+
164
+ BENCHMARK_DEFINE_F (BenchmarkFixture, Part2Benchmark)
165
+ (benchmark::State &state)
166
+ {
167
+ for (auto _ : state)
168
+ {
169
+ auto s = part2 (data);
170
+ benchmark::DoNotOptimize (s);
171
+ }
172
+ }
173
+
174
+ BENCHMARK_REGISTER_F (BenchmarkFixture, Part1Benchmark)->Unit(benchmark::kMillisecond );
175
+ BENCHMARK_REGISTER_F (BenchmarkFixture, Part2Benchmark)->Unit(benchmark::kMillisecond );
176
+
177
+ int main (int argc, char **argv)
178
+ {
179
+ Data data = parse ();
180
+
181
+ int answer1 = 0 ;
182
+ int answer2 = 0 ;
183
+
184
+ auto first = part1 (data);
185
+ std::cout << " Part 1: " << first << std::endl;
186
+
187
+ auto second = part2 (data);
188
+ std::cout << " Part 2: " << second << std::endl;
189
+
190
+ first != answer1 ? throw std::runtime_error (" Part 1 incorrect" ) : nullptr ;
191
+ second != answer2 ? throw std::runtime_error (" Part 2 incorrect" ) : nullptr ;
192
+
193
+ for (int i = 1 ; i < argc; ++i) {
194
+ if (std::string (argv[i]) == " --benchmark" ) {
195
+ benchmark::Initialize (&argc, argv);
196
+ benchmark::RunSpecifiedBenchmarks ();
197
+ return 0 ;
198
+ }
199
+ }
200
+ }
0 commit comments