From 17efdb61c40124a69927fc43c5628339c16ff9e2 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Wed, 25 Dec 2019 16:08:48 +0100 Subject: [PATCH] Improve day 20 part 1. Now use an implicit graph of nodes. This allows us to use the same graph information for part 2. Spoilers. --- 2019/src/day20.cpp | 115 +++++++++++++++++++++++++++------------------ 1 file changed, 68 insertions(+), 47 deletions(-) diff --git a/2019/src/day20.cpp b/2019/src/day20.cpp index 16042e5..d5d71b1 100644 --- a/2019/src/day20.cpp +++ b/2019/src/day20.cpp @@ -28,10 +28,8 @@ namespace { return map; } - std::pair, std::unordered_map>> - get_portals(const map_t &map) { - std::unordered_map half_portals; - std::unordered_map> portals; + auto get_portals(const map_t &map) { + std::unordered_map portals; // First find horizontal portals for (int y = 0; y < map.size(); ++y) { @@ -45,13 +43,7 @@ namespace { entry_point[0] = x + 2; } - auto name = map[y].substr(x, 2); - - if (auto it = half_portals.find(name); it != half_portals.end()) { - portals[name] = {entry_point, it->second}; - } else { - half_portals[name] = entry_point; - } + portals[entry_point] = map[y].substr(x, 2); } } } @@ -70,68 +62,97 @@ namespace { entry_point[1] = y + 2; } - if (auto it = half_portals.find(name); it != half_portals.end()) { - portals[name] = {entry_point, it->second}; - } else { - half_portals[name] = entry_point; - } + portals[entry_point] = name; } } } - return std::make_pair(half_portals, portals); + return portals; + } + + std::unordered_map>> + get_implicit_graph(const map_t &map, const std::unordered_map &portals) { + std::unordered_map half_links; + + std::unordered_map>> graph; + + for (auto &entry : portals) { + if (auto it = half_links.find(entry.second); it != half_links.end()) { + // Connect up the portals + graph[it->second].emplace_back(1, entry.first); + graph[entry.first].emplace_back(1, it->second); + } else { + half_links[entry.second] = entry.first; + } + + // Do a BFS from the node to see what we can reach. + std::deque> todo{{0, entry.first}}; + std::unordered_set visited{entry.first}; + + while (!todo.empty()) { + const auto[dist, pos] = todo.front(); + todo.pop_front(); + + for (auto &direction : DIRECTIONS) { + auto next_pos = pos + direction; + + if (map[next_pos[1]][next_pos[0]] != '.' || visited.count(next_pos)) { + continue; + } + + if (portals.count(next_pos)) { + graph[entry.first].emplace_back(dist + 1, next_pos); + } + + todo.emplace_back(dist + 1, next_pos); + visited.insert(next_pos); + } + } + } + + return graph; } } void aoc2019::day20_part1(std::istream &input, std::ostream &output) { const auto map = read_map(input); - auto[half_portals, portals] = get_portals(map); + const auto portals = get_portals(map); - const auto starting_point = half_portals.at("AA"); - const auto goal = half_portals.at("ZZ"); + const auto starting_point = std::find_if(portals.begin(), portals.end(), [](auto &x) { + return x.second == "AA"; + })->first; - std::unordered_map links; - for (auto &link : portals) { - links[link.second.first] = link.second.second; - links[link.second.second] = link.second.first; - } + auto graph = get_implicit_graph(map, portals); - std::unordered_set visited{starting_point}; - std::queue> todo; + std::unordered_set visited; + std::priority_queue, std::vector>, std::greater<>> todo; todo.emplace(0, starting_point); while (!todo.empty()) { - const auto[dist, pos] = todo.front(); + const auto[dist, pos] = todo.top(); todo.pop(); - if (pos == goal) { + if (visited.count(pos)) { + continue; + } + + visited.insert(pos); + + if (portals.at(pos) == "ZZ") { output << dist << std::endl; return; } - auto enqueue_point = [&visited, &todo, dist](point_t p) { - if (visited.count(p)) { - return; + for (auto &edge : graph[pos]) { + if (visited.count(edge.second)) { + continue; } - visited.insert(p); - todo.emplace(dist + 1, p); - }; - - for (auto &direction : DIRECTIONS) { - auto next_point = pos + direction; - if (map[next_point[1]][next_point[0]] == '.') { - enqueue_point(pos + direction); - } - } - - if (auto it = links.find(pos); it != links.end()) { - // take portal - enqueue_point(it->second); + todo.emplace(dist + edge.first, edge.second); } } - output << "Not implemented\n"; + throw std::domain_error("No valid route."); } void aoc2019::day20_part2(std::istream &input, std::ostream &output) {