From 197cb30f95faad10c4b459c3a27d8811687bdba4 Mon Sep 17 00:00:00 2001 From: Nick Krichevsky Date: Wed, 16 Dec 2020 03:28:26 -0500 Subject: [PATCH] Add day 16 part 2 soln --- day16/day16.cpp | 145 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 121 insertions(+), 24 deletions(-) diff --git a/day16/day16.cpp b/day16/day16.cpp index 7f3d8fe..17d0cb9 100644 --- a/day16/day16.cpp +++ b/day16/day16.cpp @@ -5,28 +5,58 @@ #include #include #include +#include #include #include constexpr auto NEARBY_TICKETS_HEADER = "nearby tickets:"; constexpr auto YOUR_TICKET_HEADER = "your ticket:"; -constexpr auto RANGE_PATTERN = R"((\d+)-(\d+) or (\d+)-(\d+))"; +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>; +class TicketField { + public: + TicketField(std::string &name, RangeSpec &ranges) : name(name), ranges(ranges) { + } + TicketField(std::string &&name, RangeSpec &&ranges) : name(std::move(name)), ranges(std::move(ranges)) { + } + + bool operator<(const TicketField other) const { + return this->getName() < other.getName(); + } + + bool isValueInRanges(int value) const { + auto range1 = ranges.first; + auto range2 = ranges.second; + return (value >= range1.first && value <= range1.second) || (value >= range2.first && value <= range2.second); + } + + const std::string &getName() const { + return this->name; + } + + const RangeSpec &getRanges() const { + return this->ranges; + } + + private: + std::string name; + RangeSpec ranges; +}; + class TicketSpec { public: TicketSpec( - std::vector &ourTicket, std::vector> &otherTickets, std::vector &fieldRanges) - : ourTicket(ourTicket), otherTickets(otherTickets), fieldRanges(fieldRanges) { + std::vector &ourTicket, std::vector> &otherTickets, std::vector &fields) + : ourTicket(ourTicket), otherTickets(otherTickets), fields(fields) { } TicketSpec( - std::vector &&ourTicket, std::vector> &&otherTickets, - std::vector &&fieldRanges) - : ourTicket(std::move(ourTicket)), otherTickets(std::move(otherTickets)), fieldRanges(std::move(fieldRanges)) { + std::vector &&ourTicket, std::vector> &&otherTickets, std::vector &&fields) + : ourTicket(std::move(ourTicket)), otherTickets(std::move(otherTickets)), fields(std::move(fields)) { } const std::vector &getOurTicket() const { @@ -37,14 +67,14 @@ class TicketSpec { return this->otherTickets; } - const std::vector &getFieldRanges() const { - return this->fieldRanges; + const std::vector &getFields() const { + return this->fields; } private: std::vector ourTicket; std::vector> otherTickets; - std::vector fieldRanges; + std::vector fields; }; std::vector readInput(const std::string &filename) { @@ -59,20 +89,21 @@ std::vector readInput(const std::string &filename) { } template -std::vector findRanges(Iter start, Iter end) { - std::vector pairs; - std::regex rangeExpression(RANGE_PATTERN); +std::vector parseFields(Iter start, Iter end) { + std::vector fields; + std::regex rangeExpression(FIELD_PATTERN); for (auto textIter = start; textIter != end; ++textIter) { std::smatch matches; if (!std::regex_search(*textIter, matches, rangeExpression)) { throw new std::invalid_argument("Invalid input"); } - std::pair range1(std::stoi(matches[1]), std::stoi(matches[2])); - std::pair range2(std::stoi(matches[3]), std::stoi(matches[4])); - pairs.emplace_back(std::move(range1), std::move(range2)); + std::pair range1(std::stoi(matches[2]), std::stoi(matches[3])); + std::pair range2(std::stoi(matches[4]), std::stoi(matches[5])); + RangeSpec ticketRange(std::move(range1), std::move(range2)); + fields.emplace_back(matches[1].str(), std::move(ticketRange)); } - return pairs; + return fields; } std::vector parseTicket(const std::string &rawTicket) { @@ -94,26 +125,40 @@ std::vector> parseNearbyTickets(Iter start, Iter end) { } TicketSpec parseInput(const std::vector &input) { - auto ruleEnd = std::find(input.cbegin(), input.cend(), ""); + auto fieldsEnd = std::find(input.cbegin(), input.cend(), ""); auto yourTicketBegin = std::find(input.cbegin(), input.cend(), YOUR_TICKET_HEADER); auto nearbyTicketsBegin = std::find(input.cbegin(), input.cend(), NEARBY_TICKETS_HEADER); - auto pairs = findRanges(input.begin(), ruleEnd); + auto pairs = parseFields(input.begin(), fieldsEnd); auto nearbyTickets = parseNearbyTickets(nearbyTicketsBegin + 1, input.cend()); auto yourTicket = parseTicket(*(yourTicketBegin + 1)); return TicketSpec(std::move(yourTicket), std::move(nearbyTickets), std::move(pairs)); } - bool inRangesForTicket(int value, const TicketSpec &ticketSpec) { return std::any_of( - ticketSpec.getFieldRanges().cbegin(), ticketSpec.getFieldRanges().cend(), [value](const RangeSpec &ranges) { - auto range1 = ranges.first; - auto range2 = ranges.second; - return (value >= range1.first && value <= range1.second) || - (value >= range2.first && value <= range2.second); + ticketSpec.getFields().cbegin(), ticketSpec.getFields().cend(), [value](const TicketField &field) { + return field.isValueInRanges(value); }); } +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()); + allTickets.push_back(ticketSpec.getOurTicket()); + allTickets.erase( + std::remove_if( + allTickets.begin(), + allTickets.end(), + [&ticketSpec](const std::vector &ticket) { + return !std::all_of(ticket.cbegin(), ticket.cend(), [&ticketSpec](int field) { + return inRangesForTicket(field, ticketSpec); + }); + }), + allTickets.end()); + + return allTickets; +} + int part1(const TicketSpec &ticketSpec) { return std::accumulate( ticketSpec.getOtherTickets().cbegin(), @@ -126,6 +171,57 @@ int part1(const TicketSpec &ticketSpec) { }); } +long part2(const TicketSpec &ticketSpec) { + auto validTickets = getValidTickets(ticketSpec); + std::map> fieldCandidates; + for (const TicketField &field : ticketSpec.getFields()) { + for (int i = 0; i < ticketSpec.getFields().size(); i++) { + bool matches = std::all_of(validTickets.cbegin(), validTickets.cend(), [=](const std::vector &ticket) { + return field.isValueInRanges(ticket.at(i)); + }); + if (matches) { + std::set &candidates = fieldCandidates[field]; + candidates.insert(i); + } + } + } + + std::map fieldPositions; + for (auto candidate : fieldCandidates) { + std::set valid = candidate.second; + for (auto candidate2 : fieldCandidates) { + if (candidate.first.getName() == candidate2.first.getName()) { + continue; + } + std::set difference; + std::set_difference( + valid.cbegin(), + valid.cend(), + candidate2.second.cbegin(), + candidate2.second.cend(), + std::inserter(difference, difference.end())); + // If valid is a subset of candidate, countign it is futile, and going the other direction will be more + // helpful + if (!difference.empty()) { + 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; + } + + total *= ticketSpec.getOurTicket().at(fieldPosition.second); + } + + return total; +} + int main(int argc, char *argv[]) { if (argc != 2) { std::cerr << argv[0] << " " << std::endl; @@ -136,4 +232,5 @@ int main(int argc, char *argv[]) { auto parsedInput = parseInput(input); std::cout << part1(parsedInput) << std::endl; + std::cout << part2(parsedInput) << std::endl; }