diff --git a/2022/11.cpp b/2022/11.cpp index a5e57cc..dd0f1b0 100644 --- a/2022/11.cpp +++ b/2022/11.cpp @@ -6,79 +6,87 @@ #include #include -#include +#include +#include +#include #include +#include #include namespace { +namespace qi = boost::spirit::qi; +namespace phx = boost::phoenix; + struct Monkey { std::vector items; std::int64_t divisor; std::size_t true_ix, false_ix; std::optional rhs; char op; -}; -auto Parse(std::istream & in) -> std::vector -{ - std::string word; - std::vector monkies; - - while (in >> word) { - monkies.emplace_back(); - Monkey & m = monkies.back(); - - in >> word >> word >> word; // 0: Starting items: - - for (;;) { - std::int64_t elt; - in >> elt; - m.items.push_back(elt); - - char c; - in >> c; - if (c != ',') break; - } - - in >> word >> word >> word >> word; // Operation: new = old - - in >> m.op >> word; - if (word != "old") m.rhs = std::stoll(word); - - in >> word >> word >> word >> m.divisor; - in >> word >> word >> word >> word >> word >> m.true_ix - >> word >> word >> word >> word >> word >> m.false_ix; + auto throw_to(std::int64_t const item) const -> std::size_t { + return item % divisor == 0 ? true_ix : false_ix; } - return monkies; -} + auto operation(std::int64_t const item) const -> std::int64_t { + auto const y = rhs.value_or(item); + switch (op) { + case '+': return item + y; + case '*': return item * y; + default: throw std::runtime_error{"bad input"}; + } + return item; + } +}; + +struct Grammar : public qi::grammar()> { + qi::rule monkey; + qi::rule()> monkies; + + Grammar() : base_type{monkies} { + using namespace qi::labels; + monkies = monkey % "\n"; + monkey = + "Monkey " >> + qi::omit[qi::ulong_long] >> + ":\n Starting items: " >> + (qi::long_long % ", ") [ phx::bind(&Monkey::items, _val) = _1 ] >> + "\n Operation: new = old " >> + qi::char_ [ phx::bind(&Monkey::op, _val) = _1 ] >> + " " >> + ("old" | qi::long_long [ phx::bind(&Monkey::rhs, _val) = _1 ]) >> + "\n Test: divisible by " >> + qi::long_long [ phx::bind(&Monkey::divisor, _val) = _1 ] >> + "\n If true: throw to monkey " >> + qi::ulong_long [ phx::bind(&Monkey::true_ix, _val) = _1 ] >> + "\n If false: throw to monkey " >> + qi::ulong_long [ phx::bind(&Monkey::false_ix, _val) = _1 ] >> + "\n"; + } +}; auto Solve(std::vector const& input, std::size_t const rounds, std::optional const modulus) -> std::int64_t { - std::vector throws(input.size()); - for (auto const i : boost::irange(input.size())) { - for (auto item : input[i].items) { - auto cursor = i; + auto throws = std::vector(input.size()); + for (auto const& [start, monkey] : boost::adaptors::index(input)) { + for (auto item : monkey.items) { + auto cursor = static_cast(start); for (auto n = rounds; n > 0;) { throws[cursor]++; auto const& m = input[cursor]; - auto const y = m.rhs.value_or(item); - switch (m.op) { - case '+': item += y; break; - case '*': item *= y; break; - default: throw std::runtime_error{"bad input"}; - } - if (modulus.has_value()) { + item = m.operation(item); + + if (modulus) { item %= *modulus; } else { item /= 3; } - auto const next = (item % m.divisor == 0) ? m.true_ix : m.false_ix; + auto const next = m.throw_to(item); if (next < cursor) n--; cursor = next; } @@ -100,9 +108,9 @@ auto Part2(std::vector const& input) -> std::int64_t { auto const modulus = std::transform_reduce( - input.cbegin(), input.cend(), + input.begin(), input.end(), std::int64_t{1}, std::lcm, - [](Monkey const& m) { return m.divisor; }); + [](auto const& m) { return m.divisor; }); return Solve(input, 10'000, modulus); } @@ -140,7 +148,7 @@ Monkey 3: If false: throw to monkey 1 )" }; - auto const input = Parse(in); + auto const input = aocpp::ParseGrammar(Grammar{}, in); CHECK(Part1(input) == 10'605); CHECK(Part2(input) == 2'713'310'158); } @@ -148,7 +156,7 @@ Monkey 3: auto Main(std::istream & in, std::ostream & out) -> void { - auto const input {Parse(in)}; + auto const input = aocpp::ParseGrammar(Grammar{}, in); out << "Part 1: " << Part1(input) << std::endl; out << "Part 2: " << Part2(input) << std::endl; }