diff --git a/2023/puzzle-19-01.cc b/2023/puzzle-19-01.cc new file mode 100644 index 0000000..959d492 --- /dev/null +++ b/2023/puzzle-19-01.cc @@ -0,0 +1,171 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +enum Op { greater, lessthan }; + +using Ratings = std::unordered_map; + +struct Action +{ + Action(char c, Op op, UInt limit, std::string next) + : c_(c), op_(op), limit_(limit), next_(std::move(next)) + { + } + + [[nodiscard]] auto holds(Ratings const& r) const noexcept -> bool + { + auto const it{r.find(c_)}; + assert(it != r.end()); + if (op_ == Op::greater) { return it->second > limit_; } + return it->second < limit_; + } + + [[nodiscard]] auto next() const noexcept -> std::string const& { return next_; } + +private: + char c_; + Op op_; + UInt limit_; + std::string next_; +}; + +struct Workflow +{ + void set_default(std::string const& d) { default_ = d; } + void push_action(Action const& action) { actions_.push_back(action); } + + [[nodiscard]] auto run(Ratings const& r) const noexcept -> std::string const& + { + for (auto const& action : actions_) { + if (action.holds(r)) { return action.next(); } + } + return default_; + } + +private: + std::string default_; + std::vector actions_; +}; + +struct Workflows +{ + void insert_workflow(std::string const& line) + { + auto pos{line.find('{')}; + auto name = line.substr(0, pos); + ++pos; + + Workflow workflow; + std::cout << "Workflow '" << name << "'\n"; + while (pos < line.size()) { + char const c = line[pos++]; + switch (line[pos]) { + case '>': + case '<': { + Op const op = line[pos++] == '>' ? Op::greater : Op::lessthan; + char* ppos = nullptr; + UInt const limit = std::strtoull(line.data() + pos, &ppos, 10); + pos = ppos - line.data(); + assert(line[pos] == ':'); + auto const next_start = ++pos; + while (line[++pos] != ',') { + /* loop */ + } + Action const action(c, op, limit, line.substr(next_start, pos - next_start)); + workflow.push_action(action); + std::cout << " " << c << ' ' << (op == Op::greater ? '>' : '<') << ' ' << limit << ' ' << + line.substr(next_start, pos - next_start) << '\n'; + } + break; + default: { + auto const default_start = --pos; + while (line[++pos] != '}') { + /* loop */ + } + workflow.set_default(line.substr(default_start, pos - default_start)); + std::cout << " default " << line.substr(default_start, pos - default_start) << '\n'; + } + break; + } + ++pos; + } + + workflows_.insert({name, workflow}); + } + + [[nodiscard]] auto accept(Ratings const& r) const -> bool + { + std::string rule = "in"; + while (true) { + std::cout << rule; + if (rule == "A") { return true; } + if (rule == "R") { return false; } + std::cout << " -> "; + auto it = workflows_.find(rule); + assert(it != workflows_.end()); + rule = it->second.run(r); + } + } + +private: + std::unordered_map workflows_; +}; + +auto read_ratings(std::string const& line) -> Ratings +{ + Ratings r; + std::size_t pos = 1; + while (pos < line.size()) { + char const c = line[pos++]; + assert(line[pos] == '='); + char* ppos = nullptr; + UInt const value = std::strtoull(line.data() + pos + 1, &ppos, 10); + r.insert({c, value}); + pos = ppos - line.data() + 1; + } + + return r; +} + +auto main() -> int try { + std::string line; + Workflows workflows; + + while (std::getline(std::cin, line) && !line.empty()) { + workflows.insert_workflow(line); + } + + Ratings total; + while (std::getline(std::cin, line)) { + Ratings const r = read_ratings(line); + std::cout << line << ": "; + if (workflows.accept(r)) { + for (auto [c, value] : r) { + auto [it, success] = total.insert({c, 0}); + it->second += value; + } + } + std::cout << '\n'; + } + + UInt const grand_total = std::accumulate(total.begin(), total.end(), UInt{0}, + [](UInt a, auto b) { return a + b.second; }); + + std::cout << "Total: " << grand_total << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +} diff --git a/2023/puzzle-19-02.cc b/2023/puzzle-19-02.cc new file mode 100644 index 0000000..18a2ed6 --- /dev/null +++ b/2023/puzzle-19-02.cc @@ -0,0 +1,246 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using Int = std::int64_t; +using UInt = std::uint64_t; + +enum Op { greater, lessthan }; + +using Ratings = std::unordered_map; +using Range = std::pair; +using RatingRanges = std::unordered_map; +using WorkList = std::list>; + +auto operator<<(std::ostream& os, RatingRanges const& ranges) -> std::ostream& +{ + auto const* init = "{"; + for (auto const& r : ranges) { + os << init << r.first << '[' << r.second.first << ", " << r.second.second << ')'; + init = ", "; + } + return os << "}"; +} + +struct Action +{ + Action(char c, Op op, UInt limit, std::string next) + : c_(c), op_(op), limit_(limit), next_(std::move(next)) + { + } + + void split(WorkList& work_list, RatingRanges& r) const + { + auto it{r.find(c_)}; + assert(it != r.end()); + if (op_ == Op::greater) { + RatingRanges rnew{r}; + auto itnew{rnew.find(c_)}; + itnew->second.first = std::max(itnew->second.first, limit_ + 1); + if (itnew->second.first < itnew->second.second) { + std::cout << " " << next_ << ", " << rnew << '\n'; + work_list.emplace_back(next_, rnew); + } + + it->second.second = std::min(itnew->second.second, limit_ + 1); + return; + } + + assert(op_ == Op::lessthan); + RatingRanges rnew{r}; + auto itnew{rnew.find(c_)}; + itnew->second.second = std::min(itnew->second.second, limit_); + if (itnew->second.first < itnew->second.second) { + std::cout << " " << next_ << ", " << rnew << '\n'; + work_list.emplace_back(next_, rnew); + } + + it->second.first = std::max(itnew->second.first, limit_); + } + + [[nodiscard]] auto next() const noexcept -> std::string const& { return next_; } + +private: + char c_; + Op op_; + UInt limit_; + std::string next_; +}; + +struct Workflow +{ + void set_default(std::string const& d) { default_ = d; } + void push_action(Action const& action) { actions_.push_back(action); } + + void append_ranges(WorkList& work_list, RatingRanges r) const + { + for (auto const& action : actions_) { + action.split(work_list, r); + } + + std::cout << " " << default_ << ", " << r << '\n'; + work_list.emplace_back(default_, r); + } + +private: + std::string default_; + std::vector actions_; +}; + +struct Workflows +{ + void insert_workflow(std::string const& line) + { + auto pos{line.find('{')}; + auto name = line.substr(0, pos); + ++pos; + + Workflow workflow; + while (pos < line.size()) { + char const c = line[pos++]; + switch (line[pos]) { + case '>': + case '<': { + Op const op = line[pos++] == '>' ? Op::greater : Op::lessthan; + char* ppos = nullptr; + UInt const limit = std::strtoull(line.data() + pos, &ppos, 10); + pos = ppos - line.data(); + assert(line[pos] == ':'); + auto const next_start = ++pos; + while (line[++pos] != ',') { + /* loop */ + } + Action const action(c, op, limit, line.substr(next_start, pos - next_start)); + workflow.push_action(action); + } + break; + default: { + auto const default_start = --pos; + while (line[++pos] != '}') { + /* loop */ + } + workflow.set_default(line.substr(default_start, pos - default_start)); + } + break; + } + ++pos; + } + + workflows_.insert({name, workflow}); + } + + [[nodiscard]] auto accept(Ratings const& r) const -> bool + { + RatingRanges ranges; + for (auto [c, v] : r) { + ranges.insert({c, {v, v + 1}}); + } + return total_accepted(ranges) == 1; + } + + [[nodiscard]] auto total_accepted(RatingRanges const& initial) const -> UInt + { + WorkList work_list; + work_list.emplace_back("in", initial); + UInt grand_total{0}; + + while (!work_list.empty()) { + auto [rule, range] = work_list.front(); + work_list.pop_front(); + + std::cout << " " << rule << ": " << range << '\n'; + + bool work_to_do{true}; + for (auto const& r : range) { + work_to_do &= (r.second.first < r.second.second); + } + if (!work_to_do) { + std::cout << " No work to do.\n"; + continue; + } + + if (rule == "R") { + std::cout << " Reject.\n"; + continue; + } + if (rule == "A") { + UInt total{1}; + for (auto const& r : range) { + total *= r.second.second - r.second.first; + } + grand_total += total; + std::cout << " Accept " << range << " = " << total << '\n'; + continue; + } + + auto it = workflows_.find(rule); + assert(it != workflows_.end()); + it->second.append_ranges(work_list, range); + } + + std::cout << " Grand total: " << grand_total << '\n'; + return grand_total; + } + +private: + std::unordered_map workflows_; +}; + +auto read_ratings(std::string const& line) -> Ratings +{ + Ratings r; + std::size_t pos = 1; + while (pos < line.size()) { + char const c = line[pos++]; + assert(line[pos] == '='); + char* ppos = nullptr; + UInt const value = std::strtoull(line.data() + pos + 1, &ppos, 10); + r.insert({c, value}); + pos = ppos - line.data() + 1; + } + + return r; +} + +auto main() -> int try { + std::string line; + Workflows workflows; + + while (std::getline(std::cin, line) && !line.empty()) { + workflows.insert_workflow(line); + } + + UInt total{0}; + while (std::getline(std::cin, line)) { + Ratings const r = read_ratings(line); + std::cout << line << ":\n"; + if (workflows.accept(r)) { + for (auto [c, value] : r) { + total += value; + } + } + } + std::cout << "Part 1 total: " << total << '\n'; + + RatingRanges range; + range.insert({'x', {1, 4001}}); + range.insert({'m', {1, 4001}}); + range.insert({'a', {1, 4001}}); + range.insert({'s', {1, 4001}}); + UInt const grand_total{workflows.total_accepted(range)}; + + std::cout << "Total: " << grand_total << '\n'; + + return EXIT_SUCCESS; +} +catch (...) { + std::cerr << "Uncaught exception.\n"; + return EXIT_FAILURE; +}