#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // lowercase key // uppercase door // start at @ // wall # // boring . namespace { using namespace aocpp; using Map = std::vector; using Features = std::map; using Doors = std::bitset<26>; using Distances = std::map>>; auto ReadMap(std::istream & in) -> Map { Map result; std::string line; while (std::getline(in, line)) { result.emplace_back(std::move(line)); } return result; } 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 '.': {} } } } return features; } auto FindDistancesFrom( Map const& map, Features const& features, Distances & distances, char start_letter, Coord start ) { std::map> result; std::set seen; std::deque> todo {{0, start, {}}}; while (!todo.empty()) { 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)) { 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}); } } } return result; } auto FindDistances(Map const& map, Features const& features) -> Distances { Distances distances; for (auto && [start_letter, start_coord] : features) { if (islower(start_letter) || start_letter == '@') { FindDistancesFrom(map, features, distances, start_letter, start_coord); } } return distances; } struct P1S { char location; unsigned long long keys; auto operator<=>(P1S const&) const = default; }; 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'); } auto Part1(Distances const& distances) -> std::int64_t { std::set seen; std::multimap> todo {{0, {'@', Doors()}}}; while (!todo.empty()) { auto it = todo.begin(); auto [steps, ld] = *it; auto& [l,keys] = ld; todo.erase(it); if (keys.all()) { return steps; } if (seen.insert({l,keys.to_ullong()}).second) { auto dit = distances.find(l); if (dit != distances.end()) { for (auto && [next, costneed] : dit->second) { auto [cost, need] = costneed; if ((need & (~keys)).none()) { todo.insert({steps + cost, {next, SetKey(keys, next)}}); } } } } } throw std::runtime_error{"no solution to part 1"}; } } // namespace auto main(int argc, char** argv) -> int { auto map = ReadMap(Startup(argc, argv)); auto features = FindFeatures(map); auto distances = FindDistances(map, features); std::cout << "Part 1: " << Part1(distances) << std::endl; //std::cout << "Part 2: " << part2 << std::endl; }