diff --git a/d17/main.cxx b/d17/main.cxx index a52d327..da74fbb 100644 --- a/d17/main.cxx +++ b/d17/main.cxx @@ -1,15 +1,208 @@ +#include +#include +#include #include -#include +#include +#include +#include using std::cout, std::endl; -int main() { - std::ifstream file{"../d17/input.txt"}; +constexpr const char content[] = { + #embed "input.txt" +}; + +enum class Opcode : int { + ADV = 0, + BXL, + BST, + JNZ, + BXC, + OUT, + BDV, + CDV +}; + +struct Registers { + uint64_t A; + uint64_t B; + uint64_t C; + uint64_t instruction_pointer; + std::string rstdout = ""; + + bool operator==(const Registers& other) const { + return A == other.A && B == other.B && C == other.C && instruction_pointer == other.instruction_pointer && rstdout == other.rstdout; + } +}; + +namespace std { + template <> + struct hash { + std::size_t operator()(const Registers& regs) const { + return std::hash{}(regs.A) ^ std::hash{}(regs.B) ^ std::hash{}(regs.C) ^ std::hash{}(regs.rstdout); + } + }; +} + +constexpr uint64_t resolve_combo_operand(int operand, Registers& regs) { + switch(operand) { + case 0: return 0; + case 1: return 1; + case 2: return 2; + case 3: return 3; + case 4: return regs.A; + case 5: return regs.B; + case 6: return regs.C; + default: exit(-1); + }; +} + +std::unordered_map> opcodes = { + {Opcode::ADV, [](Registers& regs, int64_t operand) { + regs.A /= std::pow(2, resolve_combo_operand(operand, regs)); + regs.instruction_pointer += 2; + }}, + {Opcode::BXL, [](Registers& regs, int64_t operand) { + regs.B ^= operand; + regs.instruction_pointer += 2; + }}, + {Opcode::BST, [](Registers& regs, int64_t operand) { + regs.B = resolve_combo_operand(operand, regs) % 8; + regs.instruction_pointer += 2; + }}, + {Opcode::JNZ, [](Registers& regs, int64_t operand) { + if(regs.A != 0) { + if(regs.instruction_pointer == operand) { + regs.instruction_pointer += 2; + } else { + regs.instruction_pointer = operand; + } + } + }}, + {Opcode::BXC, [](Registers& regs, int64_t _) { + regs.B ^= regs.C; + regs.instruction_pointer += 2; + }}, + {Opcode::OUT, [](Registers& regs, int64_t operand) { + auto val_str = std::to_string(resolve_combo_operand(operand, regs) % 8); + std::string to_print = ""; + for(int i = 0; i < val_str.size(); i++) { + to_print += val_str[i]; + to_print += ','; + } + regs.rstdout += to_print; + regs.instruction_pointer += 2; + }}, + {Opcode::BDV, [](Registers& regs, int64_t operand) { + regs.B = regs.A / std::pow(2, resolve_combo_operand(operand, regs)); + regs.instruction_pointer += 2; + }}, + {Opcode::CDV, [](Registers& regs, int64_t operand) { + regs.C = regs.A / std::pow(2, resolve_combo_operand(operand, regs)); + regs.instruction_pointer += 2; + }} +}; + +std::string part_1() { + std::istringstream input_stream(content); std::string line; - while (std::getline(file, line)) { - - } + Registers regs = {0, 0, 0, 0, ""}; + std::getline(input_stream, line); + regs.A = std::stoll(line.substr(line.find(":") + 1)); + std::getline(input_stream, line); + regs.B = std::stoll(line.substr(line.find(":") + 1)); + + std::getline(input_stream, line); + regs.C = std::stoll(line.substr(line.find(":") + 1)); + + std::vector program; + getline(input_stream, line); + getline(input_stream, line, ' '); + while(getline(input_stream, line, ',')) { + if(!line.empty()) program.push_back(std::stoi(line)); + } + + auto prev_regs = regs; + while(regs.instruction_pointer < program.size()) { + auto opcode = static_cast(program[regs.instruction_pointer]); + auto operand = program[regs.instruction_pointer + 1]; + opcodes[opcode](regs, operand); + + if(prev_regs == regs) { + regs.rstdout.pop_back(); + break; + } + prev_regs = regs; + } + + return regs.rstdout; +} + +uint64_t part_2() { + std::istringstream input_stream(content); + std::string line; + + Registers target_regs = {0, 0, 0, 0, ""}; + std::getline(input_stream, line); + target_regs.A = std::stoll(line.substr(line.find(":") + 1)); + + std::getline(input_stream, line); + target_regs.B = std::stoll(line.substr(line.find(":") + 1)); + + std::getline(input_stream, line); + target_regs.C = std::stoll(line.substr(line.find(":") + 1)); + + std::vector program; + getline(input_stream, line); + getline(input_stream, line, ' '); + while(getline(input_stream, line, ',')) { + if(!line.empty()) program.push_back(std::stoi(line)); + } + for(auto i : program) { + target_regs.rstdout += std::to_string(i) + ","; + } + target_regs.rstdout.pop_back(); + + uint64_t a_init = std::pow(8, 15); + while(a_init < std::pow(8, 16)){ + Registers regs = {a_init, 0, 0, 0, ""}; + auto prev_regs = regs; + while(regs.instruction_pointer < program.size()) { + auto opcode = static_cast(program[regs.instruction_pointer]); + auto operand = program[regs.instruction_pointer + 1]; + opcodes[opcode](regs, operand); + if(prev_regs == regs) { + regs.rstdout.pop_back(); + break; + } + prev_regs = regs; + } + + if(regs.rstdout == target_regs.rstdout) { + break; + } + + for(int i = 15; i >= 0; i--) { + if(regs.rstdout[i*2] != target_regs.rstdout[i*2]) { + a_init += std::pow(8, i); + break; + } + } + } + + return a_init; +} + +int main() { + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto p1_val = part_1(); + std::chrono::steady_clock::time_point middle = std::chrono::steady_clock::now(); + auto p2_val = part_2(); + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + cout << "Part 1: " << p1_val << " (" << std::chrono::duration_cast(middle - begin).count() << "µs)" << endl; + cout << "Part 2: " << p2_val << " (" << std::chrono::duration_cast(end - middle).count() << "µs)" << endl; return 0; } +