diff --git a/2017/18.cpp b/2017/18.cpp new file mode 100644 index 0000000..552dafd --- /dev/null +++ b/2017/18.cpp @@ -0,0 +1,206 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace { + +using Name = std::string; +using Value = std::int64_t; + +struct Literal { Value value; }; +struct Variable { Name name; }; +using Expression = std::variant; + +struct Set { Name x; Expression y; }; +struct Add { Name x; Expression y; }; +struct Mul { Name x; Expression y; }; +struct Mod { Name x; Expression y; }; +struct Jgz { Expression x; Expression y; }; +struct Rcv { Name x; }; +struct Snd { Expression x; }; +using Instruction = std::variant; + +auto IsName(std::string const& word) { + return std::all_of(word.begin(), word.end(), [](auto c) { return std::isalpha(c); }); +} + +auto ParseName(std::istream & in) -> Name +{ + std::string word; + in >> word; + if (IsName(word)) { return word; } + throw std::runtime_error{"bad name"}; +} + +auto ParseExpression(std::istream & in) -> Expression +{ + std::string word; + in >> word; + if (IsName(word)) { + return Variable{word}; + } else { + return Literal{std::stoll(word)}; + } +} + +auto Parse(std::istream & in) -> std::vector { + std::vector result; + std::string op; + while (in >> op) { + if ("set" == op) { + auto x = ParseName(in); + auto y = ParseExpression(in); + result.push_back(Set{x,y}); + } else if ("add" == op) { + auto x = ParseName(in); + auto y = ParseExpression(in); + result.push_back(Add{x,y}); + } else if ("mul" == op) { + auto x = ParseName(in); + auto y = ParseExpression(in); + result.push_back(Mul{x,y}); + } else if ("mod" == op) { + auto x = ParseName(in); + auto y = ParseExpression(in); + result.push_back(Mod{x,y}); + } else if ("jgz" == op) { + auto x = ParseExpression(in); + auto y = ParseExpression(in); + result.push_back(Jgz{x,y}); + } else if ("rcv" == op) { + auto x = ParseName(in); + result.push_back(Rcv{x}); + } else if ("snd" == op) { + auto x = ParseExpression(in); + result.push_back(Snd{x}); + } else { + throw std::runtime_error{"unknown op"}; + } + } + return result; +} + +struct Send { Value sent; }; +struct Receive { Value & target; }; +struct Halt {}; +using Effect = std::variant; + +struct Machine { + std::vector const& program_; + std::map registers_; + std::size_t pc_; + +public: + Machine(std::vector const& program) + : program_{program}, registers_{}, pc_{} + {} + + auto Eval(Expression expression) -> Value { + return std::visit(overloaded{ + [](Literal l) { return l.value; }, + [&](Variable v) { return registers_[v.name]; }, + }, expression); + } + + auto Step() -> Effect { + while (pc_ < program_.size()) { + if (auto effect = std::visit(overloaded{ + [&](Set instruction) -> std::optional { registers_[instruction.x] = Eval(instruction.y); pc_++; return {}; }, + [&](Add instruction) -> std::optional { registers_[instruction.x] += Eval(instruction.y); pc_++; return {}; }, + [&](Mul instruction) -> std::optional { registers_[instruction.x] *= Eval(instruction.y); pc_++; return {}; }, + [&](Mod instruction) -> std::optional { registers_[instruction.x] %= Eval(instruction.y); pc_++; return {}; }, + [&](Jgz instruction) -> std::optional { pc_ += Eval(instruction.x) > 0 ? Eval(instruction.y) : 1; return {}; }, + [&](Snd instruction) -> std::optional { pc_++; return Send{Eval(instruction.x)}; }, + [&](Rcv instruction) -> std::optional { pc_++; return Receive{registers_[instruction.x]}; }, + }, + program_[pc_])) + { + return *effect; + } + } + return Halt{}; + } +}; + +auto Part1(std::vector const& program) -> Value { + Value last_sound = -1; + Machine m { program }; + + for(;;) { + auto effect = m.Step(); + switch (effect.index()) { + case 0: last_sound = std::get<0>(effect).sent; break; + case 1: if (std::get<1>(effect).target > 0) { return last_sound; } break; + default: throw std::runtime_error{"program halted"}; + } + } +} + +auto Spin(Machine & m, std::deque & input, std::deque & output) -> Value* { + for(;;) { + auto effect = m.Step(); + switch (effect.index()) { + case 0: output.push_back(std::get<0>(effect).sent); break; + case 1: { + auto & target = std::get<1>(effect).target; + if (input.empty()) { + return ⌖ + } else { + target = input.front(); + input.pop_front(); + } + break; + } + default: throw std::runtime_error{"program halted"}; + } + } +} + +auto Part2(std::vector const& program) -> std::size_t { + Machine m0 { program }; + Machine m1 { program }; + m1.registers_["p"] = 1; + + std::deque inputs0; + std::deque inputs1; + + auto stuck0 = Spin(m0, inputs0, inputs1); + auto stuck1 = Spin(m1, inputs1, inputs0); + + std::size_t result = inputs0.size(); + + while(!(inputs0.empty() && inputs1.empty())) { + if (!inputs0.empty()) { + *stuck0 = inputs0.front(); inputs0.pop_front(); + stuck0 = Spin(m0, inputs0, inputs1); + } + if (!inputs1.empty()) { + *stuck1 = inputs1.front(); inputs1.pop_front(); + result -= inputs0.size(); + stuck1 = Spin(m1, inputs1, inputs0); + result += inputs0.size(); + } + } + + return result; +} + +} // namespace + +auto main(int argc, char** argv) -> int { + auto program = Parse(aocpp::Startup(argc, argv)); + std::cout << "Part 1: " << Part1(program) << std::endl; + std::cout << "Part 2: " << Part2(program) << std::endl; +} diff --git a/2017/CMakeLists.txt b/2017/CMakeLists.txt new file mode 100644 index 0000000..0bac4c1 --- /dev/null +++ b/2017/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(2017_18 18.cpp) +target_link_libraries(2017_18 aocpp) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5c33833..5a94da1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.13) set(CMAKE_C_STANDARD 11) set(CMAKE_CXX_STANDARD 20) -project(aocpp19 +project(aocpp VERSION 1 LANGUAGES C CXX ) @@ -15,9 +15,10 @@ else() endif() find_package(PkgConfig) -pkg_check_modules(GMP REQUIRED IMPORTED_TARGET gmp) +pkg_check_modules(GMP REQUIRED IMPORTED_TARGET gmpxx) add_subdirectory(lib) add_subdirectory(zmod) add_subdirectory(intcode) +add_subdirectory(2017) add_subdirectory(2019) diff --git a/lib/include/aocpp/Overloaded.hpp b/lib/include/aocpp/Overloaded.hpp new file mode 100644 index 0000000..5bce333 --- /dev/null +++ b/lib/include/aocpp/Overloaded.hpp @@ -0,0 +1,9 @@ +#ifndef AOCPP_OVERLOADED_HPP_ +#define AOCPP_OVERLOADED_HPP_ + +// helper type for the visitor #4 +template struct overloaded : Ts... { using Ts::operator()...; }; +// explicit deduction guide (not needed as of C++20) +template overloaded(Ts...) -> overloaded; + +#endif // AOCPP_OVERLOADED_HPP_ diff --git a/zmod/include/zmod.hpp b/zmod/include/zmod.hpp index b784e9d..e1cbcb4 100644 --- a/zmod/include/zmod.hpp +++ b/zmod/include/zmod.hpp @@ -21,6 +21,12 @@ public: return {value + rhs.value}; } + auto operator+=(ZMod const& rhs) -> ZMod & { + value += rhs.value; + mpz_mod_ui(this->value.get_mpz_t(), value.get_mpz_t(), Mod); + return *this; + } + auto operator-() const -> ZMod { return {-value}; } @@ -29,10 +35,22 @@ public: return {value - rhs.value}; } + auto operator-=(ZMod const& rhs) -> ZMod & { + value -= rhs.value; + mpz_mod_ui(value.get_mpz_t(), value.get_mpz_t(), Mod); + return *this; + } + auto operator*(ZMod const& rhs) const -> ZMod { return {value * rhs.value}; } + auto operator*=(ZMod const& rhs) -> ZMod & { + value -= rhs.value; + mpz_mod_ui(value.get_mpz_t(), value.get_mpz_t(), Mod); + return *this; + } + auto inverse() const -> ZMod { mpz_class m{Mod}; ZMod result; @@ -47,4 +65,4 @@ public: } -#endif \ No newline at end of file +#endif