#include #include #include #include #include #include #include #include #include #include #include #include #include using std::cout, std::endl; namespace views = std::ranges::views; constexpr const char content[] = { #embed "input.txt" }; namespace std { template struct hash> { std::size_t operator()(const std::pair& pair) const { return std::hash{}(pair.first) ^ std::hash{}(pair.second); } }; } struct Byte { int num; std::pair pos; bool operator<(const Byte& other) const { return num < other.num; } bool operator==(const Byte& other) const { return num == other.num; } bool operator!=(const Byte& other) const { return num != other.num; } }; bool out_of_bounds(int min, int max, const std::pair& pos) { auto [x, y] = pos; return x < min || y < min || x >= max || y >= max; } template std::optional shortest_path(const std::vector>& grid,const Range& bytes, const std::pair& start, const std::pair& end) { std::queue, int>> q; q.push({start, 0}); std::unordered_set> visited = {start}; while(!q.empty()) { auto [pos, steps] = q.front(); auto [x, y] = pos; q.pop(); if(pos == end) { return steps; } for(auto [dx, dy] : std::vector>{{0,1}, {0,-1}, {1,0}, {-1,0}}) { auto new_pos = std::make_pair(x + dx, y + dy); auto [nx, ny] = new_pos; auto corrupted = std::ranges::find_if(bytes, [nx, ny](const Byte& byte) { return byte.pos == std::make_pair(nx, ny); }) != bytes.end(); if(out_of_bounds(0, grid.size(), new_pos) || visited.contains(new_pos) || corrupted) { continue; } visited.insert(new_pos); q.push({new_pos, steps + 1}); } } return std::nullopt; } int part_1() { std::istringstream input_stream(content); std::string line; constexpr auto byte_falls{1024}; constexpr auto N{71}; std::vector> grid(N, std::vector(N, '.')); std::set bytes; int byte_num{0}; while(std::getline(input_stream, line) && bytes.size() < byte_falls) { Byte byte{static_cast(bytes.size()), {0, 0}}; sscanf(line.c_str(), "%d,%d", &byte.pos.second, &byte.pos.first); bytes.insert(byte); } auto start = std::make_pair(0, 0); auto end = std::make_pair(N-1, N-1); return shortest_path(grid, bytes, start, end).value(); } std::string part_2() { std::istringstream input_stream(content); std::string line; constexpr auto N{71}; std::vector> grid(N, std::vector(N, '.')); std::set bytes; while(std::getline(input_stream, line)){ auto byte = Byte{static_cast(bytes.size()), {0, 0}}; sscanf(line.c_str(), "%d,%d", &byte.pos.second, &byte.pos.first); bytes.insert(byte); } auto start = std::make_pair(0, 0); auto end = std::make_pair(N-1, N-1); int left= 0 , right = static_cast(bytes.size() - 1); int result = 0; while (left <= right) { int mid = left + (right - left) / 2; auto sub_bytes = bytes | views::take(mid + 1); if (shortest_path(grid, sub_bytes, start, end).has_value()) { left = mid + 1; } else { result = mid; right = mid - 1; } } auto byte = std::find_if(bytes.begin(), bytes.end(), [result](const Byte& byte) { return byte.num == result; }); return std::to_string(byte->pos.second) + "," + std::to_string(byte->pos.first); } int main() { std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); auto p1_val = part_1(); std::chrono::steady_clock::time_point middle = std::chrono::steady_clock::now(); auto p2_val = part_2(); std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); cout << "Part 1: " << p1_val << " (" << std::chrono::duration_cast(middle - begin).count() << "µs)" << endl; cout << "Part 2: " << p2_val << " (" << std::chrono::duration_cast(end - middle).count() << "µs)" << endl; return 0; }