#include "intcode.hpp" #include namespace intcode { BadInstruction::BadInstruction(char const* what) : std::runtime_error{what} {} Machine::Machine() : rom_{}, ram_{}, pc_{0}, base_{0} {} Machine::Machine(std::vector ram) : rom_{ram}, ram_{}, pc_{0}, base_{0} {} namespace { auto Ref(Machine & m, ValueType instruction, std::size_t k, ValueType p) -> ValueType & { auto &v = m.At(k); switch (instruction / p % 10) { case 0: return m.At(v); case 1: return v; case 2: return m.At(m.Rel(v)); default: throw BadInstruction{"invalid addressing mode"}; } } } auto Machine::At(std::size_t i) -> ValueType & { return i < rom_.size() ? rom_[i] : ram_[i]; } auto Machine::Rel(std::size_t i) -> ValueType & { return At(base_ + i); } auto Machine::Step() -> std::variant { for (;;) { auto instruction = At(pc_); auto a = [=]() -> auto & { return Ref(*this, instruction, pc_+1, 100); }; auto b = [=]() -> auto & { return Ref(*this, instruction, pc_+2, 1000); }; auto c = [=]() -> auto & { return Ref(*this, instruction, pc_+3, 10000); }; switch (instruction % 100) { case 1: c() = a() + b(); pc_ += 4; break; case 2: c() = a() * b(); pc_ += 4; break; case 3: { auto &pos = a(); pc_ += 2; return Input{pos}; } case 4: { auto val = a(); pc_ += 2; return Output{val}; } case 5: pc_ = a() ? b() : pc_ + 3; break; case 6: pc_ = a() ? pc_ + 3 : b(); break; case 7: c() = a() < b(); pc_ += 4; break; case 8: c() = a() == b(); pc_ += 4; break; case 9: base_ += a(); pc_ += 2; break; case 99: return Halt{}; default: throw BadInstruction{"invalid opcode"}; } } } auto ParseStream(std::istream &in) -> std::vector { ValueType x; std::string str; std::vector result; while (std::getline(in, str, ',')) { result.push_back(std::stol(str)); } return result; } }