#include #include #include #include #include #include #include #include #include #include #include #include #include namespace { namespace phx = boost::phoenix; namespace qi = boost::spirit::qi; struct Packet; using Packets = std::vector; using PacketPair = std::array; using PacketPairs = std::vector; // A packet is an integer or a list of packets. struct Packet { std::variant rep; }; auto operator<(Packet const& lhs, std::int64_t rhs) -> bool; auto operator<(std::int64_t lhs, Packet const& rhs) -> bool; auto operator<(std::int64_t const lhs, Packets const& rhs) -> bool { switch (rhs.size()) { case 0: return false; case 1: return lhs < rhs[0]; default: return !(rhs[0] < lhs); } } auto operator<(Packets const& lhs, std::int64_t const rhs) -> bool { switch (lhs.size()) { case 0: return true; default: return lhs[0] < rhs; } } auto operator<(Packet const& lhs, std::int64_t rhs) -> bool { return std::visit([rhs](auto const& rep) { return rep < rhs; }, lhs.rep); } auto operator<(std::int64_t lhs, Packet const& rhs) -> bool { return std::visit([lhs](auto const& rep) { return lhs < rep; }, rhs.rep); } // Less-than relation on packets. // Two integers are compared using their integer values. // Two lists are compared using a lexicographic ordering. // When an integer is compared to a list, that integer is promoted to a singleton list. auto operator<(Packet const& lhs, Packet const& rhs) -> bool { return std::visit(std::less(), lhs.rep, rhs.rep); } // Input file grammar: // Each packet should be newline-terminated. // Each pair should be newline-separated. // Packets can be integer literals or a list of packets. // A list of packets is bracketed with square brackets and is comma-separated. // No additional whitespace is tolerated in the input stream. class Grammar : public qi::grammar { qi::rule integer; qi::rule packets; qi::rule packet; qi::rule packetPair; qi::rule packetPairs; public: Grammar() : Grammar::base_type{packetPairs} { using namespace qi::labels; integer = qi::long_long; packets = "[" >> -(packet % ",") >> "]"; packet = integer [ bind(&Packet::rep, _val) = _1 ] | packets [ bind(&Packet::rep, _val) = _1 ]; packetPair = packet [_val[0] = _1] >> "\n" >> packet [_val[1] = _1] >> "\n"; packetPairs = -(packetPair % "\n"); } }; // Return the sum 1-based indexes of the pairs where the first element is less than the second. auto Part1(PacketPairs const& packetPairs) -> std::size_t { auto result = std::int64_t{0}; for (auto const& [i, pair] : boost::adaptors::index(packetPairs)) { if (pair[0] < pair[1]) { result += i + 1; } } return result; } // Returns the product of the 1-based indexes of the elements [[2]] and [[6]] // when those elements are added to all the packets in the packet pair list // and that list is then soted. auto Part2(PacketPairs const& packetPairs) -> std::size_t { auto two = Packet{2}; // Indistinguishable from [[2]] and [[6]] auto six = Packet{6}; auto ix2 = std::size_t{1}; auto ix6 = std::size_t{2}; for (auto const& pair : packetPairs) { for (auto const& Packet : pair) { if (Packet < six) { ix6++; if (Packet < two) { ix2++; } } } } return ix2 * ix6; } } // namespace TEST_SUITE("2022-13 examples") { TEST_CASE("simple ordering") { CHECK(Packet{1} < Packet{2}); CHECK(!(Packet{2} < Packet{1})); CHECK(Packet{Packets{Packet{1}}} < Packet{Packets{Packet{2}}}); CHECK(Packet{1} < Packet{Packets{Packet{2}}}); CHECK(!(Packet{Packets{Packet{2}}} < Packet{1})); CHECK(Packet{Packets{}} < Packet{Packets{Packet{2}}}); CHECK(Packet{Packets{}} < Packet{Packets{Packet{2}}}); CHECK(Packet{1} < Packet{Packets{Packet{2},Packet{3}}}); CHECK(!(Packet{Packets{Packet{2},Packet{3}}} < Packet{1})); CHECK(Packet{2} < Packet{Packets{Packet{2},Packet{3}}}); CHECK(!(Packet{Packets{Packet{2},Packet{3}}} < Packet{2})); } TEST_CASE("documented example") { auto in = std::istringstream{ "[1,1,3,1,1]\n" "[1,1,5,1,1]\n" "\n" "[[1],[2,3,4]]\n" "[[1],4]\n" "\n" "[9]\n" "[[8,7,6]]\n" "\n" "[[4,4],4,4]\n" "[[4,4],4,4,4]\n" "\n" "[7,7,7,7]\n" "[7,7,7]\n" "\n" "[]\n" "[3]\n" "\n" "[[[]]]\n" "[[]]\n" "\n" "[1,[2,[3,[4,[5,6,7]]]],8,9]\n" "[1,[2,[3,[4,[5,6,0]]]],8,9]\n" }; auto const packetPairs = aocpp::ParseGrammar(Grammar{}, in); CHECK(13 == Part1(packetPairs)); CHECK(140 == Part2(packetPairs)); } } auto Main(std::istream & in, std::ostream & out) -> void { auto const packetPairs = aocpp::ParseGrammar(Grammar{}, in); out << "Part 1: " << Part1(packetPairs) << std::endl; out << "Part 2: " << Part2(packetPairs) << std::endl; }