From 5c922f48d093fb39da112c52029c5baf03b7ae92 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Wed, 16 Dec 2020 03:38:15 -0500 Subject: [PATCH] Clean up day 16 --- day16/day16.cpp | 114 ++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 91 insertions(+), 23 deletions(-) diff --git a/day16/day16.cpp b/day16/day16.cpp index 17d0cb9..5f386cc 100644 --- a/day16/day16.cpp +++ b/day16/day16.cpp @@ -11,12 +11,16 @@ constexpr auto NEARBY_TICKETS_HEADER = "nearby tickets:"; constexpr auto YOUR_TICKET_HEADER = "your ticket:"; +constexpr auto DEPARTURE_PREFIX = "departure"; constexpr auto FIELD_PATTERN = R"((.*): (\d+)-(\d+) or (\d+)-(\d+))"; // This has to be the grossest type signature I've written in a while, but it's of // a-b or c-d maps to pair or pair using RangeSpec = std::pair, std::pair>; +/** + * A field of the ticket + */ class TicketField { public: TicketField(std::string &name, RangeSpec &ranges) : name(name), ranges(ranges) { @@ -47,6 +51,9 @@ class TicketField { RangeSpec ranges; }; +/** + * The specification of all tickets from the puzzle input + */ class TicketSpec { public: TicketSpec( @@ -88,6 +95,13 @@ std::vector readInput(const std::string &filename) { return input; } +/** + * Parse the fields of the ticket specification + * @tparam Iter The type of iterator to get the fields from + * @param start The start iterator + * @param end The end iterator + * @return std::vector All of the ticket fields from the input + */ template std::vector parseFields(Iter start, Iter end) { std::vector fields; @@ -106,6 +120,11 @@ std::vector parseFields(Iter start, Iter end) { return fields; } +/** + * Parse a single ticket + * @param rawTicket The ticket to parse + * @return std::vector The ticket's fields + */ std::vector parseTicket(const std::string &rawTicket) { std::vector ticket; folly::split(",", rawTicket, ticket); @@ -113,6 +132,13 @@ std::vector parseTicket(const std::string &rawTicket) { return ticket; } +/** + * Get all of the nearby tickets + * @tparam Iter The iterator type to get the nearby tickets + * @param start The start iterator + * @param end The end iterator + * @return std::vector> The nearby tickets and their fields + */ template std::vector> parseNearbyTickets(Iter start, Iter end) { std::vector> nearbyTickets; @@ -124,6 +150,11 @@ std::vector> parseNearbyTickets(Iter start, Iter end) { return nearbyTickets; } +/** + * Parse the puzzle input + * @param input The puzzle input + * @return TicketSpec The puzzle input as a TicketSpec + */ TicketSpec parseInput(const std::vector &input) { auto fieldsEnd = std::find(input.cbegin(), input.cend(), ""); auto yourTicketBegin = std::find(input.cbegin(), input.cend(), YOUR_TICKET_HEADER); @@ -134,6 +165,14 @@ TicketSpec parseInput(const std::vector &input) { return TicketSpec(std::move(yourTicket), std::move(nearbyTickets), std::move(pairs)); } + +/** + * Checks if a value is in range for all of the ticket's ranges + * @param value THe value to check + * @param ticketSpec The ticket specification to check against + * @return true If the value is valid + * @return false If the value is nto valid + */ bool inRangesForTicket(int value, const TicketSpec &ticketSpec) { return std::any_of( ticketSpec.getFields().cbegin(), ticketSpec.getFields().cend(), [value](const TicketField &field) { @@ -141,6 +180,11 @@ bool inRangesForTicket(int value, const TicketSpec &ticketSpec) { }); } +/** + * Get all of the valid tickets in the input + * @param ticketSpec The ticket spec from the input + * @return std::vector> The valid tickets + */ std::vector> getValidTickets(const TicketSpec &ticketSpec) { // There might be a nicer way than copying these but frankly I can't think of one that's clean. std::vector> allTickets(ticketSpec.getOtherTickets()); @@ -159,19 +203,12 @@ std::vector> getValidTickets(const TicketSpec &ticketSpec) { return allTickets; } -int part1(const TicketSpec &ticketSpec) { - return std::accumulate( - ticketSpec.getOtherTickets().cbegin(), - ticketSpec.getOtherTickets().cend(), - 0, - [&ticketSpec](int total, const std::vector &ticket) { - return total + std::accumulate(ticket.cbegin(), ticket.cend(), 0, [&ticketSpec](int subtotal, int field) { - return inRangesForTicket(field, ticketSpec) ? subtotal : subtotal + field; - }); - }); -} - -long part2(const TicketSpec &ticketSpec) { +/** + * Generate candidates of what fields could possibly be used for each position in the tickets + * @param ticketSpec The ticket specification from the problem + * @return std::map> A map of the field candidates + */ +std::map> generateFieldCandidates(const TicketSpec &ticketSpec) { auto validTickets = getValidTickets(ticketSpec); std::map> fieldCandidates; for (const TicketField &field : ticketSpec.getFields()) { @@ -186,6 +223,16 @@ long part2(const TicketSpec &ticketSpec) { } } + return fieldCandidates; +} + +/** + * Determine which fields map to what positions in the tickets + * @param ticketSpec The ticket specification from the input + * @return std::map A mapping of fields to positions in the tickets + */ +std::map determineFieldPositions(const TicketSpec &ticketSpec) { + std::map> fieldCandidates = generateFieldCandidates(ticketSpec); std::map fieldPositions; for (auto candidate : fieldCandidates) { std::set valid = candidate.second; @@ -206,20 +253,41 @@ long part2(const TicketSpec &ticketSpec) { valid = std::move(difference); } } - assert(valid.size() == 1); - fieldPositions.emplace(candidate.first, *valid.begin()); - } - long total = 1; - for (const auto &fieldPosition : fieldPositions) { - auto fieldName = fieldPosition.first.getName(); - if (fieldName.rfind("departure", 0) != 0) { - continue; + if (valid.size() != 1) { + throw new std::invalid_argument("Invalid input; not possible to determine the field mappings."); } - total *= ticketSpec.getOurTicket().at(fieldPosition.second); + fieldPositions.emplace(candidate.first, *valid.begin()); } - return total; + return fieldPositions; +} + +int part1(const TicketSpec &ticketSpec) { + return std::accumulate( + ticketSpec.getOtherTickets().cbegin(), + ticketSpec.getOtherTickets().cend(), + 0, + [&ticketSpec](int total, const std::vector &ticket) { + return total + std::accumulate(ticket.cbegin(), ticket.cend(), 0, [&ticketSpec](int subtotal, int field) { + return inRangesForTicket(field, ticketSpec) ? subtotal : subtotal + field; + }); + }); +} + +long part2(const TicketSpec &ticketSpec) { + auto fieldPositions = determineFieldPositions(ticketSpec); + return std::accumulate( + fieldPositions.cbegin(), + fieldPositions.cend(), + 1L, + [&ticketSpec](long total, std::pair fieldPosition) { + auto fieldName = fieldPosition.first.getName(); + if (fieldName.rfind(DEPARTURE_PREFIX, 0) != 0) { + return total; + } + return total * ticketSpec.getOurTicket().at(fieldPosition.second); + }); } int main(int argc, char *argv[]) {