This commit is contained in:
Eric Mertens 2022-11-10 19:38:43 -08:00
parent 6865e3594e
commit 85deec0dae

View File

@ -18,6 +18,8 @@
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
#include <aocpp/Coord.hpp> #include <aocpp/Coord.hpp>
using namespace aocpp;
// lowercase key // lowercase key
// uppercase door // uppercase door
// start at @ // start at @
@ -26,8 +28,8 @@
namespace { namespace {
using namespace aocpp; Coord(* const directions[4])(Coord) = {Up, Down, Left, Right};
using Map = std::vector<std::string>;
using Features = std::map<char, Coord>; using Features = std::map<char, Coord>;
using Doors = std::bitset<26>; using Doors = std::bitset<26>;
using Distances = std::map<char,std::map<char, std::pair<std::int64_t, Doors>>>; using Distances = std::map<char,std::map<char, std::pair<std::int64_t, Doors>>>;
@ -36,22 +38,29 @@ auto SetKey(Doors& doors, char key) {
return doors.set(std::toupper(key) - 'A'); return doors.set(std::toupper(key) - 'A');
} }
auto ReadMap(std::istream & in) -> Map { struct Map {
std::vector<std::string> 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; Map result;
std::string line; std::string line;
while (std::getline(in, line)) { while (std::getline(in, line)) {
result.emplace_back(std::move(line)); result.rows.emplace_back(std::move(line));
} }
return result; return {result};
} }
};
auto FindFeatures(Map const& map) -> Features { auto FindFeatures(Map const& map) -> Features {
std::map<char,Coord> features; Features features;
for (std::int64_t y = 0; y < map.size(); y++) { for (std::int64_t y = 0; y < map.rows.size(); y++) {
for (std::int64_t x = 0; x < map[y].size(); x++) { for (std::int64_t x = 0; x < map.rows[y].size(); x++) {
auto const c = map[y][x]; auto const c = map[{x,y}];
if ('#' != c && '.' != c) { if ('#' != c && '.' != c) {
features[c] = Coord{x,y}; features[c] = {x,y};
} }
} }
} }
@ -59,15 +68,15 @@ auto FindFeatures(Map const& map) -> Features {
} }
auto FindDistancesFrom( auto FindDistancesFrom(
Map const& map, Features const& features, Map const& map,
Features const& features,
Distances & distances, Distances & distances,
char start_letter, char const start_letter,
Coord start Coord const start
) { ) {
std::map<char, std::pair<std::int64_t, Doors>> result; std::map<char, std::pair<std::int64_t, Doors>> result;
std::deque<std::tuple<std::int64_t, Coord, Doors>> todo std::deque<std::tuple<std::int64_t, Coord, Doors>> todo {{0, start, {}}};
{{0, start, {}}};
std::set<Coord> seen; std::set<Coord> seen;
@ -77,7 +86,7 @@ auto FindDistancesFrom(
// Don't visit the same coordinate twice // Don't visit the same coordinate twice
if (!seen.insert(here).second) continue; if (!seen.insert(here).second) continue;
auto const c = map[here.y][here.x]; auto const c = map[here];
if (c == '#') continue; // avoid walls if (c == '#') continue; // avoid walls
// success, we've found a key, record the path // success, we've found a key, record the path
@ -92,7 +101,7 @@ auto FindDistancesFrom(
} }
// Visit all neighbors // Visit all neighbors
for (auto && fn : {Up,Down,Left,Right}) { for (auto const fn : directions) {
todo.emplace_back(steps+1, fn(here), doors); todo.emplace_back(steps+1, fn(here), doors);
} }
} }
@ -100,15 +109,13 @@ auto FindDistancesFrom(
return result; return result;
} }
auto FindDistances(Map const& map, Features const& features) -> Distances { auto FindDistances(Map const& map, Features const& features) {
Distances distances; Distances distances;
for (auto const [start_letter, start_coord] : features) {
for (auto && [start_letter, start_coord] : features) {
if (!std::isupper(start_letter)) { if (!std::isupper(start_letter)) {
FindDistancesFrom(map, features, distances, start_letter, start_coord); FindDistancesFrom(map, features, distances, start_letter, start_coord);
} }
} }
return distances; return distances;
} }
@ -121,11 +128,12 @@ auto SolveMaze(
using Visited = std::pair<std::string, unsigned long long>; using Visited = std::pair<std::string, unsigned long long>;
std::set<Visited> seen; std::set<Visited> seen;
using QElt = std::tuple<std::int64_t, std::string, Doors>; // Priority queue returning lowest path cost states first.
struct Cmp { auto operator()(QElt const& x, QElt const& y) -> bool { using PqElt = std::tuple<std::int64_t, std::string, Doors>;
return std::get<0>(x) > std::get<0>(y); using PqCmp = decltype([](PqElt const& x, PqElt const& y) {
}}; return std::get<0>(x) > std::get<0>(y); });
std::priority_queue<QElt, std::vector<QElt>, Cmp> todo; std::priority_queue<PqElt, std::vector<PqElt>, PqCmp> todo;
todo.emplace(0, initial_locations, Doors()); todo.emplace(0, initial_locations, Doors());
while(!todo.empty()) { while(!todo.empty()) {
@ -138,10 +146,10 @@ auto SolveMaze(
if (seen.emplace(locations, keys.to_ullong()).second) { if (seen.emplace(locations, keys.to_ullong()).second) {
for (auto& location : locations) { for (auto& location : locations) {
auto save = location; auto const save = location;
for (auto [next, costneed] : distances.at(location)) { for (auto const [next, costneed] : distances.at(location)) {
auto [cost, need] = costneed; auto const [cost, need] = costneed;
if ((need & ~keys).none()) { // no missing keys if ((need & ~keys).none()) { // no missing keys
location = next; location = next;
auto keys_ = keys; SetKey(keys_, next); auto keys_ = keys; SetKey(keys_, next);
@ -156,13 +164,12 @@ auto SolveMaze(
throw std::runtime_error{"no solution to part 1"}; throw std::runtime_error{"no solution to part 1"};
} }
auto UpdateMap(Map& map, Features & features) { // Part 2 instructs us to update the map splitting it into 4 quadrants
auto start = features['@']; auto Part2(Map& map, Features & features) {
map[start.y][start.x] = '#'; auto const start = features['@'];
map[start.y][start.x+1] = '#'; for (auto const fn : directions) {
map[start.y][start.x-1] = '#'; map[fn(start)] = '#';
map[start.y+1][start.x] = '#'; }
map[start.y-1][start.x] = '#';
features.erase('@'); features.erase('@');
features['^'] = Up(Left (start)); features['^'] = Up(Left (start));
features['&'] = Down(Left (start)); features['&'] = Down(Left (start));
@ -173,13 +180,12 @@ auto UpdateMap(Map& map, Features & features) {
} // namespace } // namespace
auto main(int argc, char** argv) -> int { auto main(int argc, char** argv) -> int {
auto map = ReadMap(Startup(argc, argv)); auto map = Map::Parse(Startup(argc, argv));
auto features = FindFeatures(map); auto features = FindFeatures(map);
auto distances = FindDistances(map, features); auto distances = FindDistances(map, features);
std::cout << "Part 1: " << SolveMaze(distances, "@") << std::endl; std::cout << "Part 1: " << SolveMaze(distances, "@") << std::endl;
UpdateMap(map, features); Part2(map, features);
distances = FindDistances(map, features); distances = FindDistances(map, features);
std::cout << "Part 2: " << SolveMaze(distances, "^&*$") << std::endl; std::cout << "Part 2: " << SolveMaze(distances, "^&*$") << std::endl;
} }