From 6865e3594e2fe89cfbbe0a94bcc19314c90c5cb2 Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Thu, 10 Nov 2022 17:46:30 -0800 Subject: [PATCH] 18 --- 2019/18.cpp | 111 +++++++++++++++++++++++++--------------------------- 1 file changed, 54 insertions(+), 57 deletions(-) diff --git a/2019/18.cpp b/2019/18.cpp index 14db007..b4cdfbf 100644 --- a/2019/18.cpp +++ b/2019/18.cpp @@ -32,6 +32,10 @@ using Features = std::map; using Doors = std::bitset<26>; using Distances = std::map>>; +auto SetKey(Doors& doors, char key) { + return doors.set(std::toupper(key) - 'A'); +} + auto ReadMap(std::istream & in) -> Map { Map result; std::string line; @@ -45,11 +49,9 @@ auto FindFeatures(Map const& map) -> Features { std::map features; for (std::int64_t y = 0; y < map.size(); y++) { for (std::int64_t x = 0; x < map[y].size(); x++) { - auto c = map[y][x]; - switch (c) { - default: features[c] = Coord{x,y}; - case '#': - case '.': {} + auto const c = map[y][x]; + if ('#' != c && '.' != c) { + features[c] = Coord{x,y}; } } } @@ -59,32 +61,39 @@ auto FindFeatures(Map const& map) -> Features { auto FindDistancesFrom( Map const& map, Features const& features, Distances & distances, - char start_letter, Coord start + char start_letter, + Coord start ) { std::map> result; - std::set seen; + std::deque> todo {{0, start, {}}}; - while (!todo.empty()) { + std::set seen; + + for (; !todo.empty(); todo.pop_front()) { auto [steps, here, doors] = todo.front(); - todo.pop_front(); - if (seen.insert(here).second) { - auto c = map[here.y][here.x]; - if (c == '#') { - continue; - } - if (c != start_letter && std::islower(c)) { + + // Don't visit the same coordinate twice + if (!seen.insert(here).second) continue; + + auto const c = map[here.y][here.x]; + if (c == '#') continue; // avoid walls + + // success, we've found a key, record the path + if (c != start_letter && std::islower(c)) { distances[start_letter][c] = {steps, doors}; - continue; - } - if (std::isupper(c)) { - doors.set(c - 'A'); - } - for (auto && fn : {Up,Down,Left,Right}) { - auto here_ = fn(here); - todo.push_back({steps+1, here_, doors}); - } + continue; // don't walk beyond the key + } + + // Note any keys we encounter on our journey + if (std::isupper(c)) { + SetKey(doors, c); + } + + // Visit all neighbors + for (auto && fn : {Up,Down,Left,Right}) { + todo.emplace_back(steps+1, fn(here), doors); } } @@ -103,53 +112,41 @@ auto FindDistances(Map const& map, Features const& features) -> Distances { return distances; } -auto HasKey(Doors doors, char key) { - return doors.test(std::toupper(key) - 'A'); -} - -auto SetKey(Doors doors, char key) { - return doors.set(std::toupper(key) - 'A'); -} - -template -auto MakePQueue(Compare const& compare) - -> std::priority_queue, Compare> -{ - return {compare}; -} - auto SolveMaze( Distances const& distances, - std::set const initial_locations + std::string const initial_locations ) -> std::int64_t { // Track current positions and current set of keys in easy to compare form - using Visited = std::pair, unsigned long long>; + using Visited = std::pair; std::set seen; - auto todo = MakePQueue, Doors>>( - [](auto const& x, auto const& y) { - return std::get<0>(x) > std::get<0>(y); - } - ); - + using QElt = std::tuple; + struct Cmp { auto operator()(QElt const& x, QElt const& y) -> bool { + return std::get<0>(x) > std::get<0>(y); + }}; + std::priority_queue, Cmp> todo; todo.emplace(0, initial_locations, Doors()); - while (!todo.empty()) { + while(!todo.empty()) { auto [steps, locations, keys] = todo.top(); todo.pop(); if (keys.all()) { return steps; } - if (seen.insert({locations,keys.to_ullong()}).second) { - for (auto location : locations) { - auto locations1 = locations; locations1.erase(location); + std::sort(locations.begin(), locations.end()); + if (seen.emplace(locations, keys.to_ullong()).second) { - for (auto && [next, costneed] : distances.at(location)) { + for (auto& location : locations) { + auto save = location; + + for (auto [next, costneed] : distances.at(location)) { auto [cost, need] = costneed; - if ((need & ~keys).none()) { - auto locations2 = locations1; locations2.insert(next); - todo.emplace(steps + cost, locations2, SetKey(keys, next)); + if ((need & ~keys).none()) { // no missing keys + location = next; + auto keys_ = keys; SetKey(keys_, next); + todo.emplace(steps + cost, locations, keys_); + location = save; } } } @@ -180,9 +177,9 @@ auto main(int argc, char** argv) -> int { auto features = FindFeatures(map); auto distances = FindDistances(map, features); - std::cout << "Part 1: " << SolveMaze(distances, {'@'}) << std::endl; + std::cout << "Part 1: " << SolveMaze(distances, "@") << std::endl; UpdateMap(map, features); distances = FindDistances(map, features); - std::cout << "Part 2: " << SolveMaze(distances, {'^', '&', '*', '$'}) << std::endl; + std::cout << "Part 2: " << SolveMaze(distances, "^&*$") << std::endl; }