From e43fca9377964876bd40f954307ca85bad58044c Mon Sep 17 00:00:00 2001 From: Matthew Gretton-Dann Date: Mon, 13 Dec 2021 11:36:48 +0000 Subject: [PATCH] Add 2016 day 25 puzzle --- 2016/puzzle-25-01.cc | 227 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 2016/puzzle-25-01.cc diff --git a/2016/puzzle-25-01.cc b/2016/puzzle-25-01.cc new file mode 100644 index 0000000..57fc5d9 --- /dev/null +++ b/2016/puzzle-25-01.cc @@ -0,0 +1,227 @@ +// +// Created by Matthew Gretton-Dann on 12/12/2021. +// + +#include +#include +#include +#include +#include +#include +#include + +enum class Register { a, b, c, d }; +auto to_register(char c) -> Register +{ + switch (c) { + case 'a': + return Register::a; + case 'b': + return Register::b; + case 'c': + return Register::c; + case 'd': + return Register::d; + default: + abort(); + } +} + +enum class Opcode { cpy, inc, dec, jnz, tgl, out }; + +auto to_opcode(std::string const& s) -> Opcode +{ + if (s == "cpy") { + return Opcode::cpy; + } + if (s == "inc") { + return Opcode::inc; + } + if (s == "dec") { + return Opcode::dec; + } + if (s == "jnz") { + return Opcode::jnz; + } + if (s == "tgl") { + return Opcode::tgl; + } + if (s == "out") { + return Opcode::out; + } + abort(); +} + +using Int = long; // NOLINT(google-runtime-int) +using Operand = std::variant; + +struct Instruction +{ + Instruction(Opcode opcode, Operand const& op1, Operand const& op2) + : opcode_(opcode), op1_(op1), op2_(op2) + { + } + + [[nodiscard]] auto opcode() const noexcept -> Opcode { return opcode_; } + [[nodiscard]] auto op1() const noexcept -> Operand const& { return op1_; } + [[nodiscard]] auto op2() const noexcept -> Operand const& { return op2_; } + + void toggle() + { + switch (opcode_) { + case Opcode::cpy: + opcode_ = Opcode::jnz; + break; + case Opcode::jnz: + opcode_ = Opcode::cpy; + break; + case Opcode::inc: + opcode_ = Opcode::dec; + break; + case Opcode::dec: + case Opcode::tgl: + case Opcode::out: + opcode_ = Opcode::inc; + break; + } + } + + static auto instruction(std::string const& s) -> Instruction + { + Opcode opcode = to_opcode(s.substr(0, 3)); + Operand op1{std::monostate()}; + Operand op2{std::monostate()}; + std::size_t idx{4}; + + { + char* end{nullptr}; + auto val{ + std::strtol(s.data() + idx, // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) + &end, 10)}; + if (end != s.data() + idx) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + op1 = val; + idx = end - s.data(); + } + else { + op1 = to_register(s[idx]); + idx += 1; + } + } + + if (idx < s.size()) { + assert(s[idx] == ' '); + ++idx; + char* end{nullptr}; + auto val{ + std::strtol(s.data() + idx, // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + &end, 10)}; + if (end != s.data() + idx) { // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic) + op2 = val; + } + else { + op2 = to_register(s[idx]); + } + } + + return {opcode, op1, op2}; + } + +private: + Opcode opcode_; + Operand op1_; + Operand op2_; +}; + +struct State +{ + explicit State(std::vector instructions) : instructions_(std::move(instructions)) {} + + [[nodiscard]] auto reg(Register r) const noexcept -> Int + { + return registers_ + [static_cast( // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) + r)]; + } + void reg(Register r, Int v) noexcept + { + registers_[static_cast( // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) + r)] = v; + } + + void execute() + { + while (pc_ < instructions_.size()) { + auto& i{instructions_[pc_]}; + switch (i.opcode()) { + case Opcode::cpy: + set(i.op2(), value(i.op1())); + break; + case Opcode::inc: + set(i.op1(), value(i.op1()) + 1); + break; + case Opcode::dec: + set(i.op1(), value(i.op1()) - 1); + break; + case Opcode::jnz: + if (value(i.op1()) != 0) { + pc_ += value(i.op2()) - 1; + } + break; + case Opcode::tgl: { + auto dest{pc_ + value(i.op1())}; + if (dest >= 0 && dest < instructions_.size()) { + instructions_[dest].toggle(); + } + } break; + case Opcode::out: + std::cout << value(i.op1()) << '\n'; + break; + } + + ++pc_; + } + } + +private: + auto value(Operand const& o) -> Int + { + if (std::holds_alternative(o)) { + return registers_ + [static_cast( // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) + std::get(o))]; + } + if (std::holds_alternative(o)) { + return std::get(o); + } + abort(); + } + + void set(Operand const& dest, Int value) + { + if (std::holds_alternative(dest)) { + registers_[ // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index) + static_cast(std::get(dest))] = value; + } + } + + std::vector instructions_; + std::array registers_{0, 0, 0, 0}; + Int pc_{0}; +}; + +auto main() -> int +{ + std::vector instructions; + std::string line; + while (std::getline(std::cin, line) && !line.empty()) { + instructions.push_back(Instruction::instruction(line)); + } + + State state(instructions); + // So it looks as if the code we're given takes the value in register A adds 2548 to it and then + // outputs the binary representation starting at the least significant bit. So we want + // (a + 2548) to have the binary representation 1010101010 - which turns out to be 182. + Int ra = 2730 - 2548; + state.reg(Register::a, ra); + state.execute(); +}