Day 10: Factory

Megathread guidelines

  • Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
  • You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL

FAQ

  • addie@feddit.uk
    link
    fedilink
    arrow-up
    4
    ·
    1 month ago

    C++

    BFS for part 1, “Z3 of shame” for part 2. On the plus side, it’s not that goddamned hailstones question from 2023 day 24 again. On the minus side, if I wanted to be reminded how bad I am at maths, then I could head on over to Project Euler and struggle with the questions there.

    Z3 is incredibly fast. Well done, Microsoft research.

    #include <boost/log/trivial.hpp>
    #include <boost/unordered/unordered_flat_map.hpp>
    #include <boost/unordered/unordered_flat_set.hpp>
    #include <fstream>
    #include <sstream>
    #include <stdexcept>
    #include <vector>
    #include <z3++.h>
    
    namespace {
    
    using Button = boost::unordered::unordered_flat_set<size_t>;
    using Joltage = std::vector<int>;
    
    struct Machine {
      std::string pattern;
      std::vector<Button> buttons;
      Joltage joltage;
    };
    
    using Machines = std::vector<Machine>;
    using PatternCount = boost::unordered::unordered_flat_map<std::string, size_t>;
    
    auto parse_button(const std::string &in) {
      auto rval = Button{};
      auto start = size_t{};
      while (start < in.size()) {
        auto comma = in.find(',', start);
        if (comma == std::string::npos)
          comma = in.size();
        rval.insert(std::stoull(in.substr(start, comma - start)));
        start = comma + 1;
      }
      return rval;
    }
    
    auto parse_joltage(const std::string &in) {
      auto rval = Joltage{};
      auto start = size_t{};
      while (start < in.size()) {
        auto comma = in.find(',', start);
        if (comma == std::string::npos)
          comma = in.size();
        rval.push_back(std::stoi(in.substr(start, comma - start)));
        start = comma + 1;
      }
      return rval;
    }
    
    auto read() {
      auto rval = Machines{};
      auto ih = std::ifstream{"10.txt"};
      if (!ih)
        throw std::runtime_error{"file?"};
      auto line = std::string{};
      while (std::getline(ih, line)) {
        auto m = Machine{};
        auto ss = std::istringstream{line};
        auto str = std::string{};
        ss >> m.pattern;
        m.pattern = m.pattern.substr(1, m.pattern.size() - 2);
        while (ss >> str) {
          if (str.at(0) == '(')
            m.buttons.push_back(parse_button(str.substr(1, str.size() - 2)));
          else
            m.joltage = parse_joltage(str.substr(1, str.size() - 2));
        }
        rval.push_back(std::move(m));
      }
      return rval;
    }
    
    auto minimum_presses(const Machine &m) {
      auto presses = size_t{};
      auto pattern = PatternCount{};
      auto first = std::string(m.pattern.size(), '.');
      auto next_iter = std::vector<std::string>{};
    
      pattern[first] = 0;
      next_iter.push_back(first);
    
      while (true) {
        auto this_iter = next_iter;
        next_iter.clear();
        ++presses;
    
        for (auto &iter : this_iter) {
          for (auto &button : m.buttons) {
            auto copy = iter;
            for (auto toggle : button)
              copy[toggle] = copy[toggle] == '.' ? '#' : '.';
            if (copy == m.pattern)
              return presses;
            if (auto f = pattern.find(copy); f != pattern.end())
              continue;
            pattern[copy] = presses;
            next_iter.push_back(std::move(copy));
          }
        }
      }
    }
    
    auto part1(const Machines &machines) {
      auto rval = size_t{};
      for (const auto &machine : machines)
        rval += minimum_presses(machine);
      return rval;
    }
    
    auto solve(const Machine &m) {
      z3::context context;
      z3::optimize opt(context);
    
      auto array = std::vector<z3::expr>{};
      auto sum = z3::expr{context.int_val(0)};
      for (auto i = size_t{}; i < m.buttons.size(); ++i) {
        auto name = std::string{"a"};
        name[0] += i;
        auto x = context.int_const(name.c_str());
        opt.add(x >= 0);
        sum = sum + x;
        array.push_back(std::move(x));
      }
      auto z = context.int_const("z");
      opt.add(sum == z);
    
      for (auto joltage = size_t{}; joltage < m.joltage.size(); ++joltage) {
        auto ex = z3::expr{context.int_val(0)};
        for (auto button = size_t{}; button < m.buttons.size(); ++button) {
          if (m.buttons.at(button).contains(joltage))
            ex = ex + array.at(button);
        }
        opt.add(ex == m.joltage.at(joltage));
      }
    
      z3::optimize::handle handle = opt.minimize(z);
      opt.check();
      return opt.lower(handle).as_uint64();
    }
    
    auto part2(const Machines &machines) {
      auto total = size_t{};
      for (auto &machine : machines)
        total += solve(machine);
      return total;
    }
    
    } // namespace
    
    auto main() -> int {
      auto machine = read();
      BOOST_LOG_TRIVIAL(info) << "Day 10: read " << machine.size();
      BOOST_LOG_TRIVIAL(info) << "1: " << part1(machine);
      BOOST_LOG_TRIVIAL(info) << "2: " << part2(machine);
    }