Clean up day 16
parent
197cb30f95
commit
5c922f48d0
114
day16/day16.cpp
114
day16/day16.cpp
|
@ -11,12 +11,16 @@
|
||||||
|
|
||||||
constexpr auto NEARBY_TICKETS_HEADER = "nearby tickets:";
|
constexpr auto NEARBY_TICKETS_HEADER = "nearby tickets:";
|
||||||
constexpr auto YOUR_TICKET_HEADER = "your ticket:";
|
constexpr auto YOUR_TICKET_HEADER = "your ticket:";
|
||||||
|
constexpr auto DEPARTURE_PREFIX = "departure";
|
||||||
constexpr auto FIELD_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
|
// 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<a, b> or pair<c ,d>
|
// a-b or c-d maps to pair<a, b> or pair<c ,d>
|
||||||
using RangeSpec = std::pair<std::pair<int, int>, std::pair<int, int>>;
|
using RangeSpec = std::pair<std::pair<int, int>, std::pair<int, int>>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A field of the ticket
|
||||||
|
*/
|
||||||
class TicketField {
|
class TicketField {
|
||||||
public:
|
public:
|
||||||
TicketField(std::string &name, RangeSpec &ranges) : name(name), ranges(ranges) {
|
TicketField(std::string &name, RangeSpec &ranges) : name(name), ranges(ranges) {
|
||||||
|
@ -47,6 +51,9 @@ class TicketField {
|
||||||
RangeSpec ranges;
|
RangeSpec ranges;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The specification of all tickets from the puzzle input
|
||||||
|
*/
|
||||||
class TicketSpec {
|
class TicketSpec {
|
||||||
public:
|
public:
|
||||||
TicketSpec(
|
TicketSpec(
|
||||||
|
@ -88,6 +95,13 @@ std::vector<std::string> readInput(const std::string &filename) {
|
||||||
return input;
|
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<TicketField> All of the ticket fields from the input
|
||||||
|
*/
|
||||||
template <typename Iter>
|
template <typename Iter>
|
||||||
std::vector<TicketField> parseFields(Iter start, Iter end) {
|
std::vector<TicketField> parseFields(Iter start, Iter end) {
|
||||||
std::vector<TicketField> fields;
|
std::vector<TicketField> fields;
|
||||||
|
@ -106,6 +120,11 @@ std::vector<TicketField> parseFields(Iter start, Iter end) {
|
||||||
return fields;
|
return fields;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a single ticket
|
||||||
|
* @param rawTicket The ticket to parse
|
||||||
|
* @return std::vector<int> The ticket's fields
|
||||||
|
*/
|
||||||
std::vector<int> parseTicket(const std::string &rawTicket) {
|
std::vector<int> parseTicket(const std::string &rawTicket) {
|
||||||
std::vector<int> ticket;
|
std::vector<int> ticket;
|
||||||
folly::split(",", rawTicket, ticket);
|
folly::split(",", rawTicket, ticket);
|
||||||
|
@ -113,6 +132,13 @@ std::vector<int> parseTicket(const std::string &rawTicket) {
|
||||||
return ticket;
|
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<std::vector<int>> The nearby tickets and their fields
|
||||||
|
*/
|
||||||
template <typename Iter>
|
template <typename Iter>
|
||||||
std::vector<std::vector<int>> parseNearbyTickets(Iter start, Iter end) {
|
std::vector<std::vector<int>> parseNearbyTickets(Iter start, Iter end) {
|
||||||
std::vector<std::vector<int>> nearbyTickets;
|
std::vector<std::vector<int>> nearbyTickets;
|
||||||
|
@ -124,6 +150,11 @@ std::vector<std::vector<int>> parseNearbyTickets(Iter start, Iter end) {
|
||||||
return nearbyTickets;
|
return nearbyTickets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse the puzzle input
|
||||||
|
* @param input The puzzle input
|
||||||
|
* @return TicketSpec The puzzle input as a TicketSpec
|
||||||
|
*/
|
||||||
TicketSpec parseInput(const std::vector<std::string> &input) {
|
TicketSpec parseInput(const std::vector<std::string> &input) {
|
||||||
auto fieldsEnd = 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 yourTicketBegin = std::find(input.cbegin(), input.cend(), YOUR_TICKET_HEADER);
|
||||||
|
@ -134,6 +165,14 @@ TicketSpec parseInput(const std::vector<std::string> &input) {
|
||||||
|
|
||||||
return TicketSpec(std::move(yourTicket), std::move(nearbyTickets), std::move(pairs));
|
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) {
|
bool inRangesForTicket(int value, const TicketSpec &ticketSpec) {
|
||||||
return std::any_of(
|
return std::any_of(
|
||||||
ticketSpec.getFields().cbegin(), ticketSpec.getFields().cend(), [value](const TicketField &field) {
|
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<std::vector<int>> The valid tickets
|
||||||
|
*/
|
||||||
std::vector<std::vector<int>> getValidTickets(const TicketSpec &ticketSpec) {
|
std::vector<std::vector<int>> getValidTickets(const TicketSpec &ticketSpec) {
|
||||||
// There might be a nicer way than copying these but frankly I can't think of one that's clean.
|
// There might be a nicer way than copying these but frankly I can't think of one that's clean.
|
||||||
std::vector<std::vector<int>> allTickets(ticketSpec.getOtherTickets());
|
std::vector<std::vector<int>> allTickets(ticketSpec.getOtherTickets());
|
||||||
|
@ -159,19 +203,12 @@ std::vector<std::vector<int>> getValidTickets(const TicketSpec &ticketSpec) {
|
||||||
return allTickets;
|
return allTickets;
|
||||||
}
|
}
|
||||||
|
|
||||||
int part1(const TicketSpec &ticketSpec) {
|
/**
|
||||||
return std::accumulate(
|
* Generate candidates of what fields could possibly be used for each position in the tickets
|
||||||
ticketSpec.getOtherTickets().cbegin(),
|
* @param ticketSpec The ticket specification from the problem
|
||||||
ticketSpec.getOtherTickets().cend(),
|
* @return std::map<TicketField, std::set<int>> A map of the field candidates
|
||||||
0,
|
*/
|
||||||
[&ticketSpec](int total, const std::vector<int> &ticket) {
|
std::map<TicketField, std::set<int>> generateFieldCandidates(const TicketSpec &ticketSpec) {
|
||||||
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 validTickets = getValidTickets(ticketSpec);
|
auto validTickets = getValidTickets(ticketSpec);
|
||||||
std::map<TicketField, std::set<int>> fieldCandidates;
|
std::map<TicketField, std::set<int>> fieldCandidates;
|
||||||
for (const TicketField &field : ticketSpec.getFields()) {
|
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<TicketField, int> A mapping of fields to positions in the tickets
|
||||||
|
*/
|
||||||
|
std::map<TicketField, int> determineFieldPositions(const TicketSpec &ticketSpec) {
|
||||||
|
std::map<TicketField, std::set<int>> fieldCandidates = generateFieldCandidates(ticketSpec);
|
||||||
std::map<TicketField, int> fieldPositions;
|
std::map<TicketField, int> fieldPositions;
|
||||||
for (auto candidate : fieldCandidates) {
|
for (auto candidate : fieldCandidates) {
|
||||||
std::set<int> valid = candidate.second;
|
std::set<int> valid = candidate.second;
|
||||||
|
@ -206,20 +253,41 @@ long part2(const TicketSpec &ticketSpec) {
|
||||||
valid = std::move(difference);
|
valid = std::move(difference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assert(valid.size() == 1);
|
if (valid.size() != 1) {
|
||||||
|
throw new std::invalid_argument("Invalid input; not possible to determine the field mappings.");
|
||||||
|
}
|
||||||
|
|
||||||
fieldPositions.emplace(candidate.first, *valid.begin());
|
fieldPositions.emplace(candidate.first, *valid.begin());
|
||||||
}
|
}
|
||||||
long total = 1;
|
|
||||||
for (const auto &fieldPosition : fieldPositions) {
|
return fieldPositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
int part1(const TicketSpec &ticketSpec) {
|
||||||
|
return std::accumulate(
|
||||||
|
ticketSpec.getOtherTickets().cbegin(),
|
||||||
|
ticketSpec.getOtherTickets().cend(),
|
||||||
|
0,
|
||||||
|
[&ticketSpec](int total, const std::vector<int> &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<TicketField, int> fieldPosition) {
|
||||||
auto fieldName = fieldPosition.first.getName();
|
auto fieldName = fieldPosition.first.getName();
|
||||||
if (fieldName.rfind("departure", 0) != 0) {
|
if (fieldName.rfind(DEPARTURE_PREFIX, 0) != 0) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
total *= ticketSpec.getOurTicket().at(fieldPosition.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
return total;
|
return total;
|
||||||
|
}
|
||||||
|
return total * ticketSpec.getOurTicket().at(fieldPosition.second);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|
Loading…
Reference in New Issue