From d26de7fd10bf805fba11a05b8666ddb24546ec1c Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Mon, 6 Dec 2021 12:55:37 +0000 Subject: [PATCH] Add 2016 day 11 puzzles --- 2016/puzzle-11-01.cc | 268 ++++++++++++++++++++++++++++++++++++++++++ 2016/puzzle-11-02.cc | 272 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 2016/puzzle-11-01.cc create mode 100644 2016/puzzle-11-02.cc diff --git a/2016/puzzle-11-01.cc b/2016/puzzle-11-01.cc new file mode 100644 index 0000000..968a4ec --- /dev/null +++ b/2016/puzzle-11-01.cc @@ -0,0 +1,268 @@ +// +// Created by Matthew Gretton-Dann on 05/12/2021. +// + +#include +#include +#include +#include +#include +#include +#include + +enum class ItemType { generator, chip }; + +struct Item +{ + auto operator<(Item const& rhs) const noexcept -> bool + { + if (name_ < rhs.name_) { + return true; + } + if (name_ == rhs.name_ && type_ < rhs.type_) { + return true; + } + return false; + } + + auto operator==(Item const& rhs) const noexcept -> bool + { + return name_ == rhs.name_ && type_ == rhs.type_; + } + + ItemType type_{ItemType::generator}; + std::string name_{}; +}; + +auto operator<<(std::ostream& os, Item const& item) -> std::ostream& +{ + switch (item.type_) { + case ItemType::generator: + os << "G"; + break; + case ItemType::chip: + os << "C"; + break; + default: + break; + } + os << item.name_; + return os; +} + +struct State +{ + static constexpr unsigned floor_count{4}; + + [[nodiscard]] auto operator==(State const& rhs) const noexcept -> bool + { + if (elevator_floor_ != rhs.elevator_floor_) { + return false; + } + for (unsigned i{0}; i < floor_count; ++i) { + if (floors_[i] != rhs.floors_[i]) { + return false; + } + } + return true; + } + + [[nodiscard]] auto operator<(State const& rhs) const noexcept -> bool + { + if (elevator_floor_ < rhs.elevator_floor_) { + return true; + } + if (elevator_floor_ > rhs.elevator_floor_) { + return false; + } + for (unsigned i{0}; i < floor_count; ++i) { + if (floors_[i] < rhs.floors_[i]) { + return true; + } + if (floors_[i] > rhs.floors_[i]) { + return false; + } + } + return false; + } + + [[nodiscard]] auto all_at_top() const noexcept -> bool + { + if (elevator_floor_ != floor_count - 1) { + return false; + } + for (unsigned i{0}; i < floor_count - 1; ++i) { + if (!floors_[i].empty()) { + return false; + } + } + return true; + } + + [[nodiscard]] auto move(int dir, Item const& item) const -> State + { + State n{*this}; + n.floors_[n.elevator_floor_].erase(item); + n.elevator_floor_ += dir; + n.floors_[n.elevator_floor_].insert(item); + return n; + } + + [[nodiscard]] auto move(int dir, Item const& i1, Item const& i2) const -> State + { + State n{*this}; + n.floors_[n.elevator_floor_].erase(i1); + n.floors_[n.elevator_floor_].erase(i2); + n.elevator_floor_ += dir; + n.floors_[n.elevator_floor_].insert(i1); + n.floors_[n.elevator_floor_].insert(i2); + return n; + } + + [[nodiscard]] auto valid() const noexcept -> bool + { + for (auto floor : floors_) { + for (auto item : floor) { + if (item.type_ != ItemType::chip) { + continue; + } + auto name = item.name_; + bool has_generator{false}; + bool has_other_generators{false}; + for (auto i2 : floor) { + if (i2.type_ == ItemType::generator && i2.name_ == name) { + has_generator = true; + } + if (i2.type_ == ItemType::generator && i2.name_ != name) { + has_other_generators = true; + } + } + if (has_other_generators && !has_generator) { + return false; + } + } + } + return true; + } + + void insert_item(unsigned floor, Item const& item) { floors_.at(floor).insert(item); } + + void print(std::ostream& os) const + { + unsigned floor{floor_count}; + while (floor-- != 0) { + std::cout << ((elevator_floor_ == floor) ? 'E' : '-'); + for (auto const& it : floors_.at(floor)) { + os << ' ' << it; + } + os << '\n'; + } + } + + using FloorState = std::set; + unsigned elevator_floor_{0}; + std::array floors_; +}; + +auto to_floor(std::string const& s) -> unsigned +{ + if (s == "first") { + return 0; + } + if (s == "second") { + return 1; + } + if (s == "third") { + return 2; + } + if (s == "fourth") { + return 3; + } + abort(); +} + +auto update_sets(std::set const& visited, std::set& to_visit, State const& s) +{ + if (s.valid() && visited.find(s) == visited.end()) { + to_visit.insert(s); + } +} + +auto main() -> int +{ + std::string line; + static std::regex floor_re{"^The ([a-z]+) floor contains "}; + static std::regex generator_re{"([a-z]+) generator"}; + static std::regex microchip_re{"([a-z]+)-compatible microchip"}; + State initial_state; + + while (std::getline(std::cin, line)) { + std::smatch m; + if (!std::regex_search(line, m, floor_re)) { + std::cerr << "unable to interpret beginning"; + return 1; + } + unsigned floor{to_floor(m.str(1))}; + std::string generators{m.suffix()}; + std::string microchips{m.suffix()}; + while (!generators.empty()) { + if (!std::regex_search(generators, m, generator_re)) { + break; + } + initial_state.insert_item(floor, {ItemType::generator, m.str(1)}); + generators = m.suffix(); + } + while (!microchips.empty()) { + if (!std::regex_search(microchips, m, microchip_re)) { + break; + } + initial_state.insert_item(floor, {ItemType::chip, m.str(1)}); + microchips = m.suffix(); + } + } + + std::set visited; + std::set to_visit; + to_visit.insert(initial_state); + unsigned cost{0}; + + while (true) { + std::cout << "Cost: " << cost << " have visited: " << visited.size() + << " visiting this time: " << to_visit.size() << '\n'; + if (to_visit.empty()) { + std::cout << "No solution found.\n"; + return 1; + } + std::set next_to_visit; + std::copy(to_visit.begin(), to_visit.end(), std::inserter(visited, visited.end())); + for (auto const& state : to_visit) { + if (state.all_at_top()) { + std::cout << "Done in " << cost << " moves\n"; + return 0; + } + + auto current_floor{state.elevator_floor_}; + for (auto i1{state.floors_[current_floor].begin()}; i1 != state.floors_[current_floor].end(); + ++i1) { + if (current_floor < State::floor_count - 1) { + update_sets(visited, next_to_visit, state.move(1, *i1)); + auto i2{i1}; + ++i2; + for (; i2 != state.floors_[current_floor].end(); ++i2) { + update_sets(visited, next_to_visit, state.move(1, *i1, *i2)); + } + } + if (current_floor > 0) { + update_sets(visited, next_to_visit, state.move(-1, *i1)); + auto i2{i1}; + ++i2; + for (; i2 != state.floors_[current_floor].end(); ++i2) { + update_sets(visited, next_to_visit, state.move(-1, *i1, *i2)); + } + } + } + } + std::swap(to_visit, next_to_visit); + ++cost; + } +} \ No newline at end of file diff --git a/2016/puzzle-11-02.cc b/2016/puzzle-11-02.cc new file mode 100644 index 0000000..2ea6806 --- /dev/null +++ b/2016/puzzle-11-02.cc @@ -0,0 +1,272 @@ +// +// Created by Matthew Gretton-Dann on 05/12/2021. +// + +#include +#include +#include +#include +#include +#include +#include + +enum class ItemType { generator, chip }; + +struct Item +{ + auto operator<(Item const& rhs) const noexcept -> bool + { + if (name_ < rhs.name_) { + return true; + } + if (name_ == rhs.name_ && type_ < rhs.type_) { + return true; + } + return false; + } + + auto operator==(Item const& rhs) const noexcept -> bool + { + return name_ == rhs.name_ && type_ == rhs.type_; + } + + ItemType type_{ItemType::generator}; + std::string name_{}; +}; + +auto operator<<(std::ostream& os, Item const& item) -> std::ostream& +{ + switch (item.type_) { + case ItemType::generator: + os << "G"; + break; + case ItemType::chip: + os << "C"; + break; + default: + break; + } + os << item.name_; + return os; +} + +struct State +{ + static constexpr unsigned floor_count{4}; + + [[nodiscard]] auto operator==(State const& rhs) const noexcept -> bool + { + if (elevator_floor_ != rhs.elevator_floor_) { + return false; + } + for (unsigned i{0}; i < floor_count; ++i) { + if (floors_[i] != rhs.floors_[i]) { + return false; + } + } + return true; + } + + [[nodiscard]] auto operator<(State const& rhs) const noexcept -> bool + { + if (elevator_floor_ < rhs.elevator_floor_) { + return true; + } + if (elevator_floor_ > rhs.elevator_floor_) { + return false; + } + for (unsigned i{0}; i < floor_count; ++i) { + if (floors_[i] < rhs.floors_[i]) { + return true; + } + if (floors_[i] > rhs.floors_[i]) { + return false; + } + } + return false; + } + + [[nodiscard]] auto all_at_top() const noexcept -> bool + { + if (elevator_floor_ != floor_count - 1) { + return false; + } + for (unsigned i{0}; i < floor_count - 1; ++i) { + if (!floors_[i].empty()) { + return false; + } + } + return true; + } + + [[nodiscard]] auto move(int dir, Item const& item) const -> State + { + State n{*this}; + n.floors_[n.elevator_floor_].erase(item); + n.elevator_floor_ += dir; + n.floors_[n.elevator_floor_].insert(item); + return n; + } + + [[nodiscard]] auto move(int dir, Item const& i1, Item const& i2) const -> State + { + State n{*this}; + n.floors_[n.elevator_floor_].erase(i1); + n.floors_[n.elevator_floor_].erase(i2); + n.elevator_floor_ += dir; + n.floors_[n.elevator_floor_].insert(i1); + n.floors_[n.elevator_floor_].insert(i2); + return n; + } + + [[nodiscard]] auto valid() const noexcept -> bool + { + for (auto floor : floors_) { + for (auto item : floor) { + if (item.type_ != ItemType::chip) { + continue; + } + auto name = item.name_; + bool has_generator{false}; + bool has_other_generators{false}; + for (auto i2 : floor) { + if (i2.type_ == ItemType::generator && i2.name_ == name) { + has_generator = true; + } + if (i2.type_ == ItemType::generator && i2.name_ != name) { + has_other_generators = true; + } + } + if (has_other_generators && !has_generator) { + return false; + } + } + } + return true; + } + + void insert_item(unsigned floor, Item const& item) { floors_.at(floor).insert(item); } + + void print(std::ostream& os) const + { + unsigned floor{floor_count}; + while (floor-- != 0) { + std::cout << ((elevator_floor_ == floor) ? 'E' : '-'); + for (auto const& it : floors_.at(floor)) { + os << ' ' << it; + } + os << '\n'; + } + } + + using FloorState = std::set; + unsigned elevator_floor_{0}; + std::array floors_; +}; + +auto to_floor(std::string const& s) -> unsigned +{ + if (s == "first") { + return 0; + } + if (s == "second") { + return 1; + } + if (s == "third") { + return 2; + } + if (s == "fourth") { + return 3; + } + abort(); +} + +auto update_sets(std::set const& visited, std::set& to_visit, State const& s) +{ + if (s.valid() && visited.find(s) == visited.end()) { + to_visit.insert(s); + } +} + +auto main() -> int +{ + std::string line; + static std::regex floor_re{"^The ([a-z]+) floor contains "}; + static std::regex generator_re{"([a-z]+) generator"}; + static std::regex microchip_re{"([a-z]+)-compatible microchip"}; + State initial_state; + + while (std::getline(std::cin, line)) { + std::smatch m; + if (!std::regex_search(line, m, floor_re)) { + std::cerr << "unable to interpret beginning"; + return 1; + } + unsigned floor{to_floor(m.str(1))}; + std::string generators{m.suffix()}; + std::string microchips{m.suffix()}; + while (!generators.empty()) { + if (!std::regex_search(generators, m, generator_re)) { + break; + } + initial_state.insert_item(floor, {ItemType::generator, m.str(1)}); + generators = m.suffix(); + } + while (!microchips.empty()) { + if (!std::regex_search(microchips, m, microchip_re)) { + break; + } + initial_state.insert_item(floor, {ItemType::chip, m.str(1)}); + microchips = m.suffix(); + } + } + initial_state.insert_item(0, {ItemType::generator, "elerium"}); + initial_state.insert_item(0, {ItemType::chip, "elerium"}); + initial_state.insert_item(0, {ItemType::generator, "dilithium"}); + initial_state.insert_item(0, {ItemType::chip, "dilithium"}); + + std::set visited; + std::set to_visit; + to_visit.insert(initial_state); + unsigned cost{0}; + + while (true) { + std::cout << "Cost: " << cost << " have visited: " << visited.size() + << " visiting this time: " << to_visit.size() << '\n'; + if (to_visit.empty()) { + std::cout << "No solution found.\n"; + return 1; + } + std::set next_to_visit; + std::copy(to_visit.begin(), to_visit.end(), std::inserter(visited, visited.end())); + for (auto const& state : to_visit) { + if (state.all_at_top()) { + std::cout << "Done in " << cost << " moves\n"; + return 0; + } + + auto current_floor{state.elevator_floor_}; + for (auto i1{state.floors_[current_floor].begin()}; i1 != state.floors_[current_floor].end(); + ++i1) { + if (current_floor < State::floor_count - 1) { + update_sets(visited, next_to_visit, state.move(1, *i1)); + auto i2{i1}; + ++i2; + for (; i2 != state.floors_[current_floor].end(); ++i2) { + update_sets(visited, next_to_visit, state.move(1, *i1, *i2)); + } + } + if (current_floor > 0) { + update_sets(visited, next_to_visit, state.move(-1, *i1)); + auto i2{i1}; + ++i2; + for (; i2 != state.floors_[current_floor].end(); ++i2) { + update_sets(visited, next_to_visit, state.move(-1, *i1, *i2)); + } + } + } + } + std::swap(to_visit, next_to_visit); + ++cost; + } +} \ No newline at end of file