#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} {} 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::Rebase(std::size_t offset) -> void { base_ += offset; } auto Machine::Next() -> ValueType & { return At(pc_++); } auto Machine::Goto(std::size_t address) -> void { pc_ = address; } auto Step(Machine & m) -> std::variant { for (;;) { auto instruction = m.Next(); auto modes = instruction / 10; auto arg = [&]() -> ValueType & { auto &v = m.Next(); switch ((modes /= 10) % 10) { case 0: return m.At(v); case 1: return v; case 2: return m.Rel(v); default: throw BadInstruction{"invalid addressing mode"}; } }; switch (instruction % 100) { case 1: { auto a = arg(); auto b = arg(); auto& c = arg(); c = a + b; break; } case 2: { auto a = arg(); auto b = arg(); auto& c = arg(); c = a * b; break; } case 3: { return Input{arg()}; } case 4: { return Output{arg()}; } case 5: { auto a = arg(); auto b = arg(); if (a) { m.Goto(b); } break; } case 6: { auto a = arg(); auto b = arg(); if (!a) { m.Goto(b); } break; } case 7: { auto a = arg(); auto b = arg(); auto& c = arg(); c = a < b; break; } case 8: { auto a = arg(); auto b = arg(); auto& c = arg(); c = a == b; break; } case 9: m.Rebase(arg()); 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; } }