From 1f73a0f28098bab3f5a0df4d0ee8e7d80ace176d Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Fri, 16 Dec 2022 16:35:22 +0000 Subject: [PATCH] Initial solution for 2022 day 16. --- 2022/puzzle-16-01.cc | 153 ++++++++++++++++++++++++++++++++ 2022/puzzle-16-02.cc | 203 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 356 insertions(+) create mode 100644 2022/puzzle-16-01.cc create mode 100644 2022/puzzle-16-02.cc diff --git a/2022/puzzle-16-01.cc b/2022/puzzle-16-01.cc new file mode 100644 index 0000000..fe03cff --- /dev/null +++ b/2022/puzzle-16-01.cc @@ -0,0 +1,153 @@ +// +// Created by Matthew Gretton-Dann on 16/12/2022. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +using namespace std::string_literals; + +struct ValveID +{ + explicit constexpr ValveID(std::string const& id) noexcept : id_((id[0] - 'A') * 26 + id[1] - 'A') + { + } + constexpr ValveID() noexcept = default; + constexpr ValveID(ValveID const&) noexcept = default; + constexpr auto operator=(ValveID const&) noexcept -> ValveID& = default; + constexpr ValveID(ValveID&&) noexcept = default; + constexpr auto operator=(ValveID&&) noexcept -> ValveID& = default; + constexpr ~ValveID() noexcept = default; + constexpr operator std::size_t() const noexcept { return id_; } + + constexpr auto operator<=>(ValveID const& r) const noexcept -> std::strong_ordering + { + return id_ <=> r.id_; + } + + static constexpr auto max() -> ValveID + { + ValveID id; + id.id_ = 26 * 26; + return id; + } + +private: + friend auto operator<<(std::ostream& os, ValveID id) -> std::ostream&; + std::uint32_t id_{std::numeric_limits::max()}; +}; + +auto operator<<(std::ostream& os, ValveID id) -> std::ostream& +{ + return os << static_cast((id.id_ / 26) + 'A') << static_cast((id.id_ % 26) + 'A'); +} + +struct State +{ + ValveID current_{"AA"s}; + UInt total_rate_{0}; + std::vector open_{std::vector(ValveID::max(), false)}; + + auto operator<=>(State const& rhs) const noexcept -> std::strong_ordering + { + if (current_ != rhs.current_) { + return current_ <=> rhs.current_; + } + for (UInt i{0}; i < open_.size(); ++i) { + if (open_[i] != rhs.open_[i]) { + return open_[i] <=> rhs.open_[i]; + } + } + return std::strong_ordering::equal; + } +}; + +auto main() -> int +{ + std::string line; + std::regex const re{ + "Valve ([A-Z][A-Z]) has flow rate=(\\d+); tunnels? leads? to valves? ([A-Z, ]+)"}; + std::vector> edges(ValveID::max(), std::list{}); + std::vector rates(ValveID::max(), 0); + UInt size{0}; + + while (std::getline(std::cin, line)) { + std::smatch m; + if (!std::regex_search(line, m, re)) { + std::cerr << "Cannot interpret: " << line << "\n"; + return EXIT_FAILURE; + } + ValveID const from{m.str(1)}; + rates.at(from) = std::stoull(m.str(2)); + std::string edge{}; + for (auto c : m.str(3)) { + if (c == ',' || c == ' ') { + continue; + } + edge += c; + if (edge.size() == 2) { + auto& l{edges.at(from)}; + auto id{ValveID{edge}}; + l.push_back(id); + ++size; + edge.clear(); + } + } + } + + std::list next_states; + std::set visited_states; + visited_states.insert(State{}); + next_states.push_back(State{}); + UInt best_rate{0}; + + for (UInt time_left{30}; time_left > 0; --time_left) { + std::list const current_states{std::move(next_states)}; + std::cout << "Time: " << time_left << ": " << current_states.size() << " states to visit.\n"; + + if (current_states.empty()) { + break; + } + + next_states.clear(); + for (auto const& state : current_states) { + if (!state.open_.at(state.current_) && rates.at(state.current_) != 0) { + State next_state{state}; + next_state.open_.at(state.current_) = true; + next_state.total_rate_ += rates.at(state.current_) * (time_left - 1); + best_rate = std::max(best_rate, next_state.total_rate_); + next_states.push_back(next_state); + visited_states.erase(next_state); + visited_states.insert(next_state); + } + + for (auto e : edges[state.current_]) { + State next_state{state}; + next_state.current_ = e; + auto vit = visited_states.find(next_state); + if (vit != visited_states.end() && vit->total_rate_ >= next_state.total_rate_) { + continue; + } + if (vit != visited_states.end()) { + visited_states.erase(vit); + } + next_states.push_back(next_state); + visited_states.insert(next_state); + } + } + } + + std::cout << "Maximum flow rate: " << best_rate << "\n"; + return EXIT_SUCCESS; +} diff --git a/2022/puzzle-16-02.cc b/2022/puzzle-16-02.cc new file mode 100644 index 0000000..5e0d482 --- /dev/null +++ b/2022/puzzle-16-02.cc @@ -0,0 +1,203 @@ +// +// Created by Matthew Gretton-Dann on 16/12/2022. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +using namespace std::string_literals; + +struct ValveID +{ + explicit constexpr ValveID(std::string const& id) noexcept : id_((id[0] - 'A') * 26 + id[1] - 'A') + { + } + explicit constexpr ValveID(UInt i) noexcept : id_(i) {} + constexpr ValveID() noexcept = default; + constexpr ValveID(ValveID const&) noexcept = default; + constexpr auto operator=(ValveID const&) noexcept -> ValveID& = default; + constexpr ValveID(ValveID&&) noexcept = default; + constexpr auto operator=(ValveID&&) noexcept -> ValveID& = default; + constexpr ~ValveID() noexcept = default; + constexpr operator std::size_t() const noexcept { return id_; } + + constexpr auto operator<=>(ValveID const& r) const noexcept -> std::strong_ordering + { + return id_ <=> r.id_; + } + + static constexpr auto max() -> ValveID + { + ValveID id; + id.id_ = 26 * 26; + return id; + } + +private: + friend auto operator<<(std::ostream& os, ValveID id) -> std::ostream&; + std::uint32_t id_{std::numeric_limits::max()}; +}; + +auto operator<<(std::ostream& os, ValveID id) -> std::ostream& +{ + return os << static_cast((id.id_ / 26) + 'A') << static_cast((id.id_ % 26) + 'A'); +} + +struct State +{ + ValveID id_[2]{ValveID("AA"s), ValveID("AA"s)}; + UInt next_time_[2]{26, 26}; + UInt total_rate_{0}; + std::vector open_{std::vector(ValveID::max(), false)}; + + auto operator<=>(State const& rhs) const noexcept -> std::strong_ordering + { + for (unsigned i = 0; i < 2; ++i) { + if (id_[i] != rhs.id_[i]) { + return id_[i] <=> rhs.id_[i]; + } + if (next_time_[i] != rhs.next_time_[i]) { + return next_time_[i] <=> rhs.next_time_[i]; + } + } + for (UInt i{0}; i < open_.size(); ++i) { + if (open_[i] != rhs.open_[i]) { + return open_[i] <=> rhs.open_[i]; + } + } + return std::strong_ordering::equal; + } +}; + +auto main() -> int +{ + std::string line; + std::regex const re{ + "Valve ([A-Z][A-Z]) has flow rate=(\\d+); tunnels? leads? to valves? ([A-Z, ]+)"}; + std::vector> edges(ValveID::max(), std::vector(ValveID::max(), 0)); + std::vector rates(ValveID::max(), 0); + UInt size{0}; + + while (std::getline(std::cin, line)) { + std::smatch m; + if (!std::regex_search(line, m, re)) { + std::cerr << "Cannot interpret: " << line << "\n"; + return EXIT_FAILURE; + } + ValveID const from{m.str(1)}; + rates.at(from) = std::stoull(m.str(2)); + std::string edge{}; + for (auto c : m.str(3)) { + if (c == ',' || c == ' ') { + continue; + } + edge += c; + if (edge.size() == 2) { + edges.at(from).at(ValveID{edge}) = 1; + edge.clear(); + } + } + } + + bool changed{true}; + while (changed) { + changed = false; + for (UInt e1{0}; e1 < ValveID::max(); ++e1) { + for (UInt e2{0}; e2 < ValveID::max(); ++e2) { + if (edges[e1][e2] != 0) { + for (UInt e3{0}; e3 < ValveID::max(); ++e3) { + if (edges[e2][e3] == 1 && edges[e1][e3] == 0) { + edges[e1][e3] = edges[e1][e2] + 1; + changed = true; + } + } + } + } + } + } + + // Turning default state on. + for (UInt id{0}; id < ValveID::max(); ++id) { + edges[id][id] = 0; + } + + // If rate at a site is zero then change the cost to zero. + for (UInt e1{0}; e1 < ValveID::max(); ++e1) { + for (UInt e2{0}; e2 < ValveID::max(); ++e2) { + if (rates[e2] == 0) { + edges[e1][e2] = 0; + } + } + } + + std::list current_states; + std::set visited_states; + visited_states.insert(State{}); + current_states.push_back(State{}); + UInt best_rate{0}; + + for (UInt time_left{26}; time_left > 0; --time_left) { + for (std::size_t mover{0}; mover < 2; ++mover) { + std::cout << "Time: " << time_left << ": " << current_states.size() + << " states to visit. Person " << mover << "\n"; + + if (current_states.empty()) { + break; + } + + for (auto it{current_states.begin()}; it != current_states.end();) { + if (it->next_time_[mover] != time_left) { + ++it; + continue; + } + + ValveID from{it->id_[mover]}; + + for (UInt to{0}; to < ValveID::max(); ++to) { + if (edges[from][to] == 0) { + continue; + } + if (edges[from][to] + 1 >= time_left) { + continue; + } + if (it->open_[to]) { + continue; + } + + State next_state{*it}; + next_state.id_[mover] = ValveID(to); + next_state.open_[to] = true; + auto timestep = edges[from][to] + 1; + next_state.next_time_[mover] -= timestep; + next_state.total_rate_ += (next_state.next_time_[mover]) * rates[to]; + best_rate = std::max(best_rate, next_state.total_rate_); + + auto vit = visited_states.find(next_state); + if (vit != visited_states.end() && vit->total_rate_ >= next_state.total_rate_) { + continue; + } + if (vit != visited_states.end()) { + visited_states.erase(vit); + } + visited_states.insert(next_state); + current_states.push_back(next_state); + } + it = current_states.erase(it); + } + } + } + + std::cout << "Maximum flow rate: " << best_rate << "\n"; + return EXIT_SUCCESS; +}