diff --git a/2019/12.cpp b/2019/12.cpp new file mode 100644 index 0000000..0ed5cc8 --- /dev/null +++ b/2019/12.cpp @@ -0,0 +1,103 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +struct Particle { + std::int64_t pos; + std::int64_t vel; + auto operator==(Particle const& x) const -> bool = default; +}; + +auto Step(std::vector & ps) { + // apply gravity to update velocities + auto n = ps.size(); + for (int i = 0; i+1 < n; i++) { + for (int j = i+1; j < n; j++) { + auto v = (ps[i].pos < ps[j].pos) - (ps[i].pos > ps[j].pos); + ps[i].vel += v; + ps[j].vel -= v; + } + } + // apply velocities to update positions + for (auto & p : ps) { + p.pos += p.vel; + } +} + +auto CycleLength(std::vector const& particles) -> std::int64_t { + auto ps = particles; + std::int64_t n = 0; + do { + Step(ps); + n++; + } while (!std::equal(ps.begin(), ps.end(), particles.begin())); + return n; +} + +/// Parse input as a vector of particle systems by axis. +auto Parse(std::istream & in) -> std::array,3> { + std::array,3> result; + std::string line; + while (std::getline(in, line)) { + std::int64_t x, y, z; + auto res = + std::sscanf( + line.c_str(), + "", + &x, &y, &z); + if (3 != res) { throw std::runtime_error{"bad input"}; } + result[0].push_back({x,0}); + result[1].push_back({y,0}); + result[2].push_back({z,0}); + } + return result; +} + +/// Compute part 1 by iterating the simulation step and compute the resulting +/// system energy. +auto Part1(std::array, 3> system, std::int64_t const n) { + for (auto && ps : system) { + for (std::int64_t i = 0; i < n; i++) { Step(ps); } + } + + std::int64_t result = 0; + for (std::size_t i = 0; i < system[0].size(); i++) { + std::int64_t xsum = 0; + std::int64_t vsum = 0; + for (auto && ps : system) { + xsum += std::abs(ps[i].pos); + vsum += std::abs(ps[i].vel); + } + result += xsum * vsum; + } + return result; +} + +auto Part2(std::array, 3> const& ps) { + std::int64_t n = 1; + for (auto && p : ps) { + n = std::lcm(n, CycleLength(p)); + } + return n; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto ps = Parse(aocpp::Startup(argc, argv)); + auto part2 = Part2(ps); + auto part1 = Part1(std::move(ps), 1000); + + std::cout << "Part 1: " << part1 << std::endl; + std::cout << "Part 2: " << part2 << std::endl; +} diff --git a/2019/14.cpp b/2019/14.cpp new file mode 100644 index 0000000..571dd91 --- /dev/null +++ b/2019/14.cpp @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace { + +using Recipes = std::map>>>; + +auto Parse(std::istream & in) -> Recipes { + Recipes result; + + std::string line; + + std::int64_t n; + std::string word; + + while (std::getline(in, line)) { + std::vector> components; + std::istringstream sin {line}; + + bool leftSide; + do { + sin >> n >> word; + leftSide = word.back() == ','; + if (leftSide) word.pop_back(); + components.push_back({n, word}); + } while(leftSide); + sin >> word; // skip + sin >> n >> word; + result.insert({word, {n, std::move(components)}}); + } + + return result; +} + +auto Topsort(Recipes const& recipes) -> std::vector +{ + // Kahn's algorithm + std::set perm; + std::set temp; + std::vector result; + + auto visit_ = [&](auto& rec, std::string name) -> void { + if (!perm.contains(name)) { + if (!temp.insert(name).second) throw std::runtime_error{"cyclic recipes"}; + if (recipes.contains(name)) { + for (auto && [_, c] : recipes.at(name).second) { + rec(rec, c); + } + } + temp.erase(name); + perm.insert(name); + result.push_back(name); + } + }; + auto visit = [&](auto name) { visit_(visit_, name); }; + + for (auto && [k,_] : recipes) { + visit(k); + } + + std::reverse(result.begin(), result.end()); + + return result; +} + +class Machine { + struct Entry { + std::int64_t need; + std::int64_t batch; + std::vector> components; + }; + std::int64_t ore_; + std::vector process_; +public: + Machine(Recipes const& recipes) + { + auto order = Topsort(recipes); + order.pop_back(); + process_.resize(order.size(), {}); + + std::map indexes; + for (auto && name : order) { + indexes.insert({name, indexes.size()}); + } + + for (auto && [k,v] : recipes) { + auto & entry = process_[indexes[k]]; + entry.batch = v.first; + for (auto && [m,c] : v.second) { + entry.components.push_back({m, c == "ORE" ? ore_ : process_[indexes[c]].need}); + } + } + } + + auto operator()(std::int64_t fuel) { + for (auto && entry : process_) { + entry.need = 0; + } + process_[0].need = fuel; + ore_ = 0; + + for (auto && entry : process_) { + auto batches = (entry.need + entry.batch - 1) / entry.batch; + for (auto && [m,c] : entry.components) { + c += batches*m; + } + } + + return ore_; + } +}; + +auto ComputeFuel(Machine & machine, std::int64_t ore) { + std::int64_t tooHi = 1; + while (machine(tooHi) <= ore) { + tooHi *= 2; + } + + std::int64_t lo = 0; + while (lo+1 != tooHi) { + auto mid = std::midpoint(lo, tooHi); + (machine(mid) > ore ? tooHi : lo) = mid; + } + + return lo; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto recipes = Parse(aocpp::Startup(argc, argv)); + auto machine = Machine(recipes); + auto part1 = machine(1); + auto part2 = ComputeFuel(machine, 1'000'000'000'000); + std::cout << "Part 1: " << part1 << std::endl; + std::cout << "Part 2: " << part2 << std::endl; +} diff --git a/2019/CMakeLists.txt b/2019/CMakeLists.txt index 3841493..c76d1cf 100644 --- a/2019/CMakeLists.txt +++ b/2019/CMakeLists.txt @@ -34,6 +34,9 @@ target_link_libraries(12 aocpp) add_executable(13 13.cpp) target_link_libraries(13 aocpp intcode) +add_executable(14 14.cpp) +target_link_libraries(14 aocpp) + add_executable(15 15.cpp) target_link_libraries(15 aocpp intcode)