From 9c8226dfaa3de257115f4bd8878ba7f09985efc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20M=C3=A5rdbrink?= Date: Wed, 18 Dec 2024 12:22:56 +0100 Subject: [PATCH] Day 18 --- d18/main.cxx | 144 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 6 deletions(-) diff --git a/d18/main.cxx b/d18/main.cxx index dd9f8df..5987560 100644 --- a/d18/main.cxx +++ b/d18/main.cxx @@ -1,15 +1,147 @@ +#include +#include +#include +#include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include using std::cout, std::endl; +namespace views = std::ranges::views; -int main() { - std::ifstream file{"../d18/input.txt"}; +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; - while (std::getline(file, 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; }