From 17728edb4429d6724004c40ad3d59195e649fb6f Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Thu, 10 Nov 2022 21:01:24 -0800 Subject: [PATCH] 20 --- 2019/20.cpp | 175 ++++++++++++++++++++++++++++++++++++++++++++ 2019/CMakeLists.txt | 3 + 2 files changed, 178 insertions(+) create mode 100644 2019/20.cpp diff --git a/2019/20.cpp b/2019/20.cpp new file mode 100644 index 0000000..95525cc --- /dev/null +++ b/2019/20.cpp @@ -0,0 +1,175 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace aocpp; + +namespace { + +Coord(* const directions[4])(Coord) = {Up, Down, Left, Right}; + +using Name = std::string; +using Portals = std::map; +using Distances = std::map>>; + +struct Map { + std::vector rows; + + auto operator[](Coord c) -> char& { return rows[c.y][c.x]; } + auto operator[](Coord c) const -> char { return rows[c.y][c.x]; } + + static auto Parse(std::istream & in) -> Map { + Map result; + std::string line; + while (std::getline(in, line)) { + result.rows.emplace_back(std::move(line)); + } + return {result}; + } +}; + +auto FindPortals(Map const& map) -> Portals { + Portals portals; + + std::int64_t w = map.rows[0].size(); + std::int64_t h = map.rows.size(); + + for (std::int64_t x = 1; x < w-1; x++) { + for (std::int64_t y = 1; y < h-1; y++) { + Coord const c = {x,y}; + auto const v = map[c]; + if (std::isupper(v)) { + char polarity = x==1 || x==w-2 || y==1 || y==h-2 + ? '-' : '+'; + if (map[Up(c)] == '.') { + portals[{polarity, v, map[Down(c)]}] = Up(c); + } else if (map[Down(c)] == '.') { + portals[{polarity, map[Up(c)], v}] = Down(c); + } else if (map[Left(c)] == '.') { + portals[{polarity, v, map[Right(c)]}] = Left(c); + } else if (map[Right(c)] == '.') { + portals[{polarity, map[Left(c)], v}] = Right(c); + } + } + } + } + return portals; +} + +auto FindDistancesFrom( + Map const& map, + std::map const& names, + Distances & distances, + std::string const start_name, + Coord const start +) { + std::vector> result; + + std::deque> todo {{0, start}}; + + std::set seen; + + for (; !todo.empty(); todo.pop_front()) { + auto const [steps, here] = todo.front(); + + // Don't visit the same coordinate twice + if (!seen.insert(here).second) continue; + + auto const c = map[here]; + if (c != '.') continue; // avoid walls + + // success, we've found a key, record the path + if (auto it = names.find(here); it != names.end() && it->second != start_name) { + result.emplace_back(it->second, steps); + continue; // don't walk beyond the portal + } + + // Visit all neighbors + for (auto const fn : directions) { + todo.emplace_back(steps+1, fn(here)); + } + } + + return result; +} + +auto OtherName(Name name) { + name[0] = name[0] == '+' ? '-' : '+'; + return name; +} + +auto FindDistances(Map const& map, Portals const& portals) { + Distances distances; + + std::map names; + for (auto [k,v] : portals) { + names[v] = k; + } + + for (auto const [start_name, start_coord] : portals) { + distances[start_name] = FindDistancesFrom(map, names, distances, start_name, start_coord); + } + + return distances; +} + +auto SolveMaze(Distances const& distances, bool const recursive) -> std::int64_t +{ + // Track current positions and current set of keys in easy to compare form + using Visited = std::pair; + std::set seen; + + // Priority queue returning lowest path cost states first. + using PqElt = std::tuple; + using PqCmp = decltype([](PqElt const& x, PqElt const& y) { + return std::get<0>(x) > std::get<0>(y); }); + std::priority_queue, PqCmp> todo; + + todo.emplace(0, 0, "-AA"); + + while(!todo.empty()) { + auto [steps, depth, name] = todo.top(); + todo.pop(); + if (name == "-ZZ") { return steps; } + if (seen.emplace(depth, name).second) { + if (auto const it = distances.find(name); it != distances.end()) { + for (auto const [next, cost] : it->second) { + if (next == "-ZZ") { + if (depth == 0) todo.emplace(steps + cost, depth, "-ZZ"); + } else { + auto const depth_ = depth + (recursive ? (next[0]=='+' ? 1 : -1) : 0); + if (depth_ >= 0) todo.emplace(steps + cost + 1, depth_, OtherName(next)); + } + } + } + } + } + throw std::runtime_error{"no solution"}; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto map = Map::Parse(Startup(argc, argv)); + auto portals = FindPortals(map); + auto distances = FindDistances(map, portals); + + std::cout << "Part 1: " << SolveMaze(distances, false) << std::endl; + std::cout << "Part 2: " << SolveMaze(distances, true) << std::endl; +} diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 504fee5..173a95a 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -43,5 +43,8 @@ target_link_libraries(15 aocpp intcode) add_executable(18 18.cpp) target_link_libraries(18 aocpp) +add_executable(20 20.cpp) +target_link_libraries(20 aocpp) + add_executable(23 23.cpp) target_link_libraries(23 aocpp intcode)