diff --git a/2022/puzzle-19-01.cc b/2022/puzzle-19-01.cc index 3269447..3f18cd1 100644 --- a/2022/puzzle-19-01.cc +++ b/2022/puzzle-19-01.cc @@ -11,7 +11,6 @@ #include #include #include -#include using Int = std::int64_t; using UInt = std::uint64_t; @@ -53,22 +52,42 @@ struct State std::array robots_available_; }; +struct StateCompare +{ + auto operator()(State const& lhs, State const& rhs) const noexcept -> bool + { + for (UInt r{0}; r < resource_count; ++r) { + if (lhs.resources_available_[r] < rhs.resources_available_[r]) { + return true; + } + if (lhs.resources_available_[r] > rhs.resources_available_[r]) { + return false; + } + if (lhs.robots_available_[r] < rhs.robots_available_[r]) { + return true; + } + if (lhs.robots_available_[r] > rhs.robots_available_[r]) { + return false; + } + } + return false; + } +}; + +using StateSet = std::set; + auto generate(Costs const& costs) -> UInt { UInt max_geode_count{0}; constexpr UInt total_time{24}; - std::list next_states{State()}; + StateSet next_states; + next_states.insert(State{}); for (UInt t{0}; t < total_time; ++t) { - std::cout << " Time " << t << ": " << next_states.size() - << " States to consider. Max geode: " << max_geode_count << "\n"; - std::list const current_states{std::move(next_states)}; - next_states = std::list(); + StateSet const current_states{std::move(next_states)}; + next_states = StateSet{}; for (auto const& state : current_states) { - // See if this is the best state we've seen: - max_geode_count = std::max(max_geode_count, state.resources_available_[GEODE]); - // Let's build a robot: for (UInt robot{0}; robot < resource_count; ++robot) { bool build{true}; @@ -84,7 +103,11 @@ auto generate(Costs const& costs) -> UInt new_state.resources_available_[resource] += state.robots_available_[resource]; } new_state.robots_available_[robot] += 1; - next_states.push_back(new_state); + auto [it, success] = next_states.insert(new_state); + if (success) { + // See if this is the best state we've seen: + max_geode_count = std::max(max_geode_count, new_state.resources_available_[GEODE]); + } } } @@ -93,9 +116,15 @@ auto generate(Costs const& costs) -> UInt for (UInt resource{0}; resource < resource_count; ++resource) { new_state.resources_available_[resource] += state.robots_available_[resource]; } - next_states.push_back(new_state); + auto [it, success] = next_states.insert(new_state); + if (success) { + // See if this is the best state we've seen: + max_geode_count = std::max(max_geode_count, new_state.resources_available_[GEODE]); + } } } + + return max_geode_count; } auto main() -> int diff --git a/2022/puzzle-19-02.cc b/2022/puzzle-19-02.cc new file mode 100644 index 0000000..088eb36 --- /dev/null +++ b/2022/puzzle-19-02.cc @@ -0,0 +1,169 @@ +// +// Created by Matthew Gretton-Dann on 16/12/2022. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int32_t; +using UInt = std::uint32_t; + +enum Resources { Ore, Clay, Obsidian, Geode }; +UInt constexpr ORE{0}; +UInt constexpr CLAY{1}; +UInt constexpr OBSIDIAN{2}; +UInt constexpr GEODE{3}; +UInt constexpr resource_count{4}; + +struct Costs +{ + Costs() noexcept : costs_() + { + for (auto& r : costs_) { + for (auto& c : r) { + c = 0; + } + } + } + Costs(Costs const&) noexcept = default; + Costs(Costs&&) noexcept = default; + auto operator=(Costs const&) noexcept -> Costs& = default; + auto operator=(Costs&&) noexcept -> Costs& = default; + ~Costs() = default; + + std::array, resource_count> costs_; +}; + +struct State +{ + State() : resources_available_({0, 0, 0, 0}), robots_available_({1, 0, 0, 0}) {} + + std::array resources_available_; + std::array robots_available_; +}; + +struct StateCompare +{ + auto operator()(State const& lhs, State const& rhs) const noexcept -> bool + { + for (UInt r{0}; r < resource_count; ++r) { + if (lhs.resources_available_[r] < rhs.resources_available_[r]) { + return true; + } + if (lhs.resources_available_[r] > rhs.resources_available_[r]) { + return false; + } + if (lhs.robots_available_[r] < rhs.robots_available_[r]) { + return true; + } + if (lhs.robots_available_[r] > rhs.robots_available_[r]) { + return false; + } + } + return false; + } +}; + +using StateSet = std::set; + +auto generate(Costs const& costs) -> UInt +{ + constexpr UInt total_time{32}; + StateSet next_states; + next_states.insert(State{}); + + for (UInt t{0}; t < total_time; ++t) { + std::cout << "Time " << t << ": " << next_states.size() << " states to consider.\n"; + StateSet const current_states{std::move(next_states)}; + next_states = StateSet{}; + + for (auto const& state : current_states) { + // Let's build a robot: + auto built{0}; + auto robots{0}; + for (UInt robot{0}; robot < resource_count; ++robot) { + if (state.robots_available_[robot] != 0) { + ++robots; + } + bool build{true}; + for (UInt resource{0}; resource < resource_count; ++resource) { + if (costs.costs_[robot][resource] > state.resources_available_[resource]) { + build = false; + } + } + if (build) { + ++built; + State new_state{state}; + for (UInt resource{0}; resource < resource_count; ++resource) { + new_state.resources_available_[resource] -= costs.costs_[robot][resource]; + new_state.resources_available_[resource] += state.robots_available_[resource]; + } + new_state.robots_available_[robot] += 1; + next_states.insert(new_state); + } + } + + if (built != robots) { + // Now build a robot that just collects: + State new_state{state}; + for (UInt resource{0}; resource < resource_count; ++resource) { + new_state.resources_available_[resource] += state.robots_available_[resource]; + } + next_states.insert(new_state); + } + } + } + + auto const& max_elt = + *std::max_element(next_states.begin(), next_states.end(), [](auto const& l, auto const& r) { + return l.resources_available_[GEODE] < r.resources_available_[GEODE]; + }); + + return max_elt.resources_available_[GEODE]; +} + +auto main() -> int +{ + std::string line; + std::map cost_map; + std::regex const re{"Blueprint (\\d+): Each ore robot costs (\\d+) ore. Each clay robot costs " + "(\\d+) ore. Each obsidian robot costs (\\d+) ore and (\\d+) clay. Each " + "geode robot costs (\\d+) ore and (\\d+) obsidian."}; + + 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; + } + + UInt id{static_cast(std::stoul(m.str(1)))}; + Costs costs; + costs.costs_[ORE][ORE] = static_cast(std::stoul(m.str(2))); + costs.costs_[CLAY][ORE] = static_cast(std::stoul(m.str(3))); + costs.costs_[OBSIDIAN][ORE] = static_cast(std::stoul(m.str(4))); + costs.costs_[OBSIDIAN][CLAY] = static_cast(std::stoul(m.str(5))); + costs.costs_[GEODE][ORE] = static_cast(std::stoul(m.str(6))); + costs.costs_[GEODE][OBSIDIAN] = static_cast(std::stoul(m.str(7))); + + cost_map.insert({id, costs}); + } + + UInt geode_count{1}; + for (UInt i{1}; i <= 3; ++i) { + auto costs{cost_map.find(i)}; + auto result{generate(costs->second)}; + std::cout << "For ID " << costs->first << " result is: " << result << "\n"; + geode_count *= result; + } + + std::cout << "Total quality: " << geode_count << "\n"; + return EXIT_SUCCESS; +}