Files
advent-of-code/2020/puzzle-16-02.cc

201 lines
5.0 KiB
C++

#include <algorithm>
#include <array>
#include <cassert>
#include <iostream>
#include <list>
#include <map>
#include <regex>
#include <string>
struct Classifier
{
void add_ranges(std::string const& s)
{
static const std::regex re("(.+): (\\d+)-(\\d+) or (\\d+)-(\\d+)");
std::smatch m;
if (std::regex_search(s, m, re)) {
auto field = field_names_.size();
field_names_.push_back(m.str(1));
std::cout << m.str(1) << "valid = ";
add_range(m.str(2), m.str(3), field);
std::cout << ", ";
add_range(m.str(4), m.str(5), field);
std::cout << "\n";
}
}
void add_range(std::string const& lows, std::string const& highs, std::size_t field)
{
int low = std::stoi(lows);
int high = std::stoi(highs);
++high;
unsigned flag = 1 << field;
assert(flag != 0);
if (num_to_valid_field_.size() < high) {
num_to_valid_field_.resize(high, 0);
}
std::cout << "[" << low << ", " << high << "]";
while (low < high) {
num_to_valid_field_[low] |= flag;
++low;
}
}
void close()
{
idx_to_valid_field_.resize(0);
idx_to_valid_field_.resize(field_names_.size(), (1 << field_names_.size()) - 1);
}
int count_errors(std::string const& s) const
{
std::string::size_type pos = 0;
int result = 0;
while (pos < s.size()) {
std::size_t len = 0;
int num = std::stoi(s.substr(pos), &len);
if (num >= num_to_valid_field_.size() || num_to_valid_field_[num] == 0) {
result += num == 0 ? 1 : num;
std::cout << "Invalid: " << num << "\n";
}
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
}
return result;
}
void mark_valid(std::string const& s)
{
std::string::size_type pos = 0;
unsigned idx = 0;
while (pos < s.size()) {
std::size_t len = 0;
int num = std::stoi(s.substr(pos), &len);
unsigned valid_fields = num >= num_to_valid_field_.size() ? 0 : num_to_valid_field_[num];
idx_to_valid_field_[idx] &= valid_fields;
pos += len;
while (pos < s.size() && s[pos] == ',') {
++pos;
}
assert(idx_to_valid_field_[idx] != 0);
++idx;
}
}
void print_valid() const
{
for (std::size_t idx = 0; idx < idx_to_valid_field_.size(); ++idx) {
std::cout << "Index " << idx << " valid for fields:";
unsigned field = 0;
for (auto valid_fields = idx_to_valid_field_[idx]; valid_fields != 0; valid_fields >>= 1) {
if (valid_fields & 1) {
std::cout << " " << field_names_[field];
}
++field;
}
std::cout << "\n";
}
}
void reduce()
{
bool changed = true;
while (changed) {
changed = false;
for (unsigned idx = 0; idx < idx_to_valid_field_.size(); ++idx) {
auto valid_fields = idx_to_valid_field_[idx];
if ((valid_fields & (valid_fields - 1)) == 0) {
std::cout << "Index " << idx << " can only be field " << valid_fields << "\n";
for (unsigned idx2 = 0; idx2 < idx_to_valid_field_.size(); ++idx2) {
if (idx == idx2) {
continue;
}
changed |= (idx_to_valid_field_[idx2] & valid_fields) != 0;
idx_to_valid_field_[idx2] &= ~valid_fields;
}
}
}
}
}
unsigned long calculate_product(std::string const& s, std::string const& prefix)
{
std::vector<unsigned> values;
std::size_t pos = 0;
while (pos < s.size()) {
if (s[pos] == ',') {
++pos;
}
else {
std::size_t len;
values.push_back(std::stoi(s.substr(pos), &len));
pos += len;
}
}
unsigned long product = 1;
for (unsigned field = 0; field < field_names_.size(); ++field) {
if (field_names_[field].substr(0, prefix.size()) == prefix) {
unsigned idx = 0;
while (idx < idx_to_valid_field_.size() && idx_to_valid_field_[idx] != (1 << field)) {
++idx;
}
assert(idx < idx_to_valid_field_.size());
product *= values[idx];
}
}
return product;
}
std::vector<unsigned> num_to_valid_field_;
std::vector<unsigned> idx_to_valid_field_;
std::vector<std::string> field_names_;
};
enum class State { Permitted, Your, Nearby };
int main(int argc, char** argv)
{
std::string line;
State s = State::Permitted;
Classifier c;
int e = 0;
std::string our_ticket;
while (std::getline(std::cin, line)) {
if (line == "your ticket:") {
s = State::Your;
}
else if (line == "nearby tickets:") {
s = State::Nearby;
c.close();
}
else if (line == "") {
/* nothing */;
}
else if (s == State::Permitted) {
c.add_ranges(line);
}
else if (s == State::Your) {
our_ticket = line;
}
else if (s == State::Nearby) {
auto e2 = c.count_errors(line);
e += e2;
if (e2 == 0) {
c.mark_valid(line);
}
}
}
c.print_valid();
c.reduce();
c.print_valid();
std::cout << "Product: " << c.calculate_product(our_ticket, "departure ") << "\n";
return 0;
}