From 06909578ea11aa460c232588061bdcf0db875315 Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Thu, 16 Dec 2021 16:35:43 +0000 Subject: [PATCH] Add 2017 day 18 puzzles. --- 2017/puzzle-18-01.cc | 158 ++++++++++++++++++++++++++++++++++++ 2017/puzzle-18-02.cc | 189 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 347 insertions(+) create mode 100644 2017/puzzle-18-01.cc create mode 100644 2017/puzzle-18-02.cc diff --git a/2017/puzzle-18-01.cc b/2017/puzzle-18-01.cc new file mode 100644 index 0000000..b8c8807 --- /dev/null +++ b/2017/puzzle-18-01.cc @@ -0,0 +1,158 @@ +#include +#include +#include +#include +#include + +using Int = long; +using Register = char; +using Operand = std::variant; +enum class Opcode { snd, set, add, mul, mod, rcv, jgz }; + +auto to_opcode(std::string const& s) -> Opcode +{ + if (s == "snd") { + return Opcode::snd; + } + if (s == "set") { + return Opcode::set; + } + if (s == "add") { + return Opcode::add; + } + if (s == "mul") { + return Opcode::mul; + } + if (s == "mod") { + return Opcode::mod; + } + if (s == "rcv") { + return Opcode::rcv; + } + if (s == "jgz") { + return Opcode::jgz; + } + abort(); +} + +static auto operand(std::string const& s, std::size_t idx) -> std::pair +{ + if (idx >= s.size()) { + return {std::monostate{}, idx}; + } + if (s[idx] >= 'a' && s[idx] <= 'z') { + return {s[idx], idx + 1}; + } + + std::size_t end{0}; + auto v{std::stol(s.substr(idx), &end)}; + return {v, idx + end}; +} + +struct Instruction +{ + auto opcode() const noexcept -> Opcode { return opcode_; } + auto op1() const noexcept -> Operand { return op1_; } + auto op2() const noexcept -> Operand { return op2_; } + + static auto instruction(std::string const& s) -> Instruction + { + auto opcode{to_opcode(s.substr(0, 3))}; + auto [op1, idx] = operand(s, 4); + auto [op2, idx2] = operand(s, idx + 1); + return {opcode, op1, op2}; + } + +private: + Instruction(Opcode opcode, Operand op1, Operand op2) : opcode_(opcode), op1_(op1), op2_(op2) {} + + Opcode opcode_; + Operand op1_; + Operand op2_; +}; + +using Instructions = std::vector; + +struct State +{ + explicit State(Instructions const& instructions) : instructions_(instructions) {} + + void execute() + { + while (pc_ >= 0 && pc_ < instructions_.size()) { + auto const& instruction{instructions_[pc_]}; + switch (instruction.opcode()) { + case Opcode::add: + set(instruction.op1(), value(instruction.op1()) + value(instruction.op2())); + break; + case Opcode::jgz: + if (value(instruction.op1()) > 0) { + pc_ += value(instruction.op2()) - 1; + } + break; + case Opcode::mod: + set(instruction.op1(), value(instruction.op1()) % value(instruction.op2())); + break; + case Opcode::mul: + set(instruction.op1(), value(instruction.op1()) * value(instruction.op2())); + break; + case Opcode::set: + set(instruction.op1(), value(instruction.op2())); + break; + case Opcode::snd: + last_freq_ = value(instruction.op1()); + break; + case Opcode::rcv: + if (last_freq_ != 0) { + std::cout << "Recovered frequency: " << last_freq_ << '\n'; + std::exit(0); + } + break; + default: + abort(); + } + ++pc_; + } + } + +private: + void set(Operand const& op, Int value) + { + if (std::holds_alternative(op)) { + registers_.insert_or_assign(std::get(op), value); + } + else { + abort(); + } + } + + auto value(Operand const& op) const -> Int + { + if (std::holds_alternative(op)) { + auto it{registers_.find(std::get(op))}; + return it == registers_.end() ? 0 : it->second; + } + if (std::holds_alternative(op)) { + return std::get(op); + } + abort(); + } + + Instructions instructions_; + std::map registers_; + Int pc_{0}; + Int last_freq_{0}; +}; + +auto main() -> int +{ + Instructions instructions; + std::string line; + while (std::getline(std::cin, line) && !line.empty()) { + instructions.push_back(Instruction::instruction(line)); + } + + State cpu{instructions}; + cpu.execute(); + return 0; +} diff --git a/2017/puzzle-18-02.cc b/2017/puzzle-18-02.cc new file mode 100644 index 0000000..51a774a --- /dev/null +++ b/2017/puzzle-18-02.cc @@ -0,0 +1,189 @@ +#include +#include +#include +#include +#include + +using Int = long; +using Register = char; +using Operand = std::variant; +using Messages = std::vector; +enum class Opcode { snd, set, add, mul, mod, rcv, jgz }; + +auto to_opcode(std::string const& s) -> Opcode +{ + if (s == "snd") { + return Opcode::snd; + } + if (s == "set") { + return Opcode::set; + } + if (s == "add") { + return Opcode::add; + } + if (s == "mul") { + return Opcode::mul; + } + if (s == "mod") { + return Opcode::mod; + } + if (s == "rcv") { + return Opcode::rcv; + } + if (s == "jgz") { + return Opcode::jgz; + } + abort(); +} + +static auto operand(std::string const& s, std::size_t idx) -> std::pair +{ + if (idx >= s.size()) { + return {std::monostate{}, idx}; + } + if (s[idx] >= 'a' && s[idx] <= 'z') { + return {s[idx], idx + 1}; + } + + std::size_t end{0}; + auto v{std::stol(s.substr(idx), &end)}; + return {v, idx + end}; +} + +struct Instruction +{ + auto opcode() const noexcept -> Opcode { return opcode_; } + auto op1() const noexcept -> Operand { return op1_; } + auto op2() const noexcept -> Operand { return op2_; } + + static auto instruction(std::string const& s) -> Instruction + { + auto opcode{to_opcode(s.substr(0, 3))}; + auto [op1, idx] = operand(s, 4); + auto [op2, idx2] = operand(s, idx + 1); + return {opcode, op1, op2}; + } + +private: + Instruction(Opcode opcode, Operand op1, Operand op2) : opcode_(opcode), op1_(op1), op2_(op2) {} + + Opcode opcode_; + Operand op1_; + Operand op2_; +}; + +using Instructions = std::vector; + +struct State +{ + explicit State(Instructions const& instructions) : instructions_(instructions) {} + + auto out_queue_begin() const noexcept -> Messages::const_iterator { return out_queue_.begin(); } + auto out_queue_end() const noexcept -> Messages::const_iterator { return out_queue_.end(); } + auto out_queue_size() const noexcept -> Messages::size_type { return out_queue_.size(); } + + void set(Register reg, Int value) { registers_.insert_or_assign(reg, value); } + + template + auto execute(It in_queue_begin, It in_queue_end) -> It + { + while (pc_ >= 0 && pc_ < instructions_.size()) { + auto const& instruction{instructions_[pc_]}; + switch (instruction.opcode()) { + case Opcode::add: + set(instruction.op1(), value(instruction.op1()) + value(instruction.op2())); + break; + case Opcode::jgz: + if (value(instruction.op1()) > 0) { + pc_ += value(instruction.op2()) - 1; + } + break; + case Opcode::mod: + set(instruction.op1(), value(instruction.op1()) % value(instruction.op2())); + break; + case Opcode::mul: + set(instruction.op1(), value(instruction.op1()) * value(instruction.op2())); + break; + case Opcode::set: + set(instruction.op1(), value(instruction.op2())); + break; + case Opcode::snd: + out_queue_.push_back(value(instruction.op1())); + break; + case Opcode::rcv: + if (in_queue_begin == in_queue_end) { + return in_queue_begin; + } + set(instruction.op1(), *(in_queue_begin++)); + break; + default: + abort(); + } + ++pc_; + } + + return in_queue_begin; + } + +private: + void set(Operand const& op, Int value) + { + if (std::holds_alternative(op)) { + set(std::get(op), value); + } + else { + abort(); + } + } + + auto value(Operand const& op) const -> Int + { + if (std::holds_alternative(op)) { + auto it{registers_.find(std::get(op))}; + return it == registers_.end() ? 0 : it->second; + } + if (std::holds_alternative(op)) { + return std::get(op); + } + abort(); + } + + Instructions instructions_; + std::map registers_; + Messages out_queue_; + Int pc_{0}; +}; + +auto main() -> int +{ + Instructions instructions; + std::string line; + while (std::getline(std::cin, line) && !line.empty()) { + instructions.push_back(Instruction::instruction(line)); + } + + State cpu0{instructions}; + cpu0.set('p', 0); + State cpu1{instructions}; + cpu1.set('p', 1); + + bool cont = true; + std::size_t queue_used0{0}; + std::size_t queue_used1{0}; + while (cont) { + /* Iterate over each CPU in turn, monitoring how queue sizes change. */ + auto queue1{cpu0.execute(cpu1.out_queue_begin() + queue_used1, cpu1.out_queue_end())}; + auto next_queue_used1{std::distance(cpu1.out_queue_begin(), queue1)}; + auto queue0{cpu1.execute(cpu0.out_queue_begin() + queue_used0, cpu0.out_queue_end())}; + auto next_queue_used0{std::distance(cpu0.out_queue_begin(), queue0)}; + if (next_queue_used0 == queue_used0 && next_queue_used1 == queue_used1) { + /* We have deadlocked/finished if neither queue has changed in size this iteration. */ + std::cout << "CPU 0 out queue size: " << cpu0.out_queue_size() << '\n'; + std::cout << "CPU 1 out queue size: " << cpu1.out_queue_size() << '\n'; + return 0; + } + queue_used0 = next_queue_used0; + queue_used1 = next_queue_used1; + } + return 0; +}