#include #include #include #include #include enum InstructionType { INP, ADD, MUL, DIV, MOD, EQL }; class Instruction { public: Instruction(InstructionType type, int operand1, int operand2, bool op2_var) : type(type), operand1(operand1), operand2(operand2), operand2_is_variable(op2_var){}; void performInstruction(std::vector ®isters, std::queue &input) { auto op2 = operand2_is_variable ? registers[operand2] : operand2; switch(type) { case INP: registers[operand1] = input.front(); input.pop(); break; case ADD: registers[operand1] += op2; break; case MUL: registers[operand1] *= op2; break; case DIV: registers[operand1] /= op2; break; case MOD: registers[operand1] %= op2; break; case EQL: registers[operand1] = registers[operand1] == op2 ? 1 : 0; break; } } int getOperand1() const { return operand1; } int getOperand2() const { return operand2; } private: InstructionType type; int operand1 = -1; int operand2 = -1; bool operand2_is_variable = false; }; class ALU { public: ALU() { registers = {0,0,0,0}; } void addInstruction(const Instruction &instruction) { instructions.push_back(instruction); } void addInput(int64_t in) { input.push(in); } void reset() { registers = {0,0,0,0}; while(!input.empty()) { input.pop(); } } void performProgram() { for(auto &instruction : instructions) { instruction.performInstruction(registers, input); } } int64_t getRegisterValue(int register_index) { return registers[register_index]; } const std::vector &getInstructions() const { return instructions; } private: std::vector instructions; std::vector registers; std::queue input; }; ALU getALU(const std::string &file_name) { std::ifstream file(file_name); ALU result; std::string str; while (std::getline(file, str)) { InstructionType type; int operand1 = 0; int operand2 = 0; bool operand2_is_var = false; if(str.substr(0, 3) == "add") { type = ADD; } else if(str.substr(0, 3) == "mul") { type = MUL; } else if(str.substr(0, 3) == "inp") { type = INP; } else if(str.substr(0, 3) == "div") { type = DIV; } else if(str.substr(0, 3) == "mod") { type = MOD; } else if(str.substr(0, 3) == "eql") { type = EQL; } switch(str[4]) { case 'w': operand1 = 0; break; case 'x': operand1 = 1; break; case 'y': operand1 = 2; break; case 'z': operand1 = 3; break; } if(str[6] > '9') { switch(str[6]) { case 'w': operand2 = 0; break; case 'x': operand2 = 1; break; case 'y': operand2 = 2; break; case 'z': operand2 = 3; break; } operand2_is_var = true; } else { std::string tmp; std::stringstream ss(str); ss >> tmp; ss >> tmp; ss >> operand2; } result.addInstruction(Instruction(type, operand1, operand2, operand2_is_var)); } return result; } int64_t inputToInt(const int input[14]) { int64_t result = 0; for(int i = 0; i < 14; i++) { result *= 10; result += input[i]; } return result; } void decreaseInput(int input[14]) { int index = 13; bool cont = false; do { cont = false; input[index] -= 1; if(input[index] == 0) { input[index] = 9; index--; cont = true; if(index < 0) { return; } } } while(cont); if(index < 8) { std::cout << inputToInt(input) << std::endl; } } int64_t part1(ALU &alu) { int input[14] = {9,9,9,9,9,9,9,9,9,9,9,9,9,9}; int vars[14][2] = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; auto instructions = alu.getInstructions(); int index = 0; for(int i = 5; i < instructions.size(); i+=18) { vars[index][0] = instructions[i].getOperand2(); vars[index][1] = instructions[i+10].getOperand2(); index++; } std::vector push; for(int i = 0; i < 14; i++) { if(vars[i][0] > 0) { push.push_back(i); } else { auto in = input[push.back()] + vars[push.back()][1]; in += vars[i][0]; if(in < 0) { std::cerr << "Something went horribly wrong" << std::endl; exit(1); } while(in > 9) { in--; input[push.back()]--; } push.pop_back(); input[i] = in; } } alu.reset(); for(int i = 0; i < 14; i++) { alu.addInput(input[i]); } alu.performProgram(); if(alu.getRegisterValue(3) == 0) { return inputToInt(input); } return -1; } uint64_t part2(ALU &alu) { int input[14] = {1,1,1,1,1,1,1,1,1,1,1,1,1,1}; int vars[14][2] = {{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0},{0,0}}; auto instructions = alu.getInstructions(); int index = 0; for(int i = 5; i < instructions.size(); i+=18) { vars[index][0] = instructions[i].getOperand2(); vars[index][1] = instructions[i+10].getOperand2(); index++; } std::vector push; for(int i = 0; i < 14; i++) { if(vars[i][0] > 0) { push.push_back(i); } else { auto in = input[push.back()] + vars[push.back()][1]; in += vars[i][0]; if(in > 9) { std::cerr << "Something went horribly wrong" << std::endl; exit(1); } while(in < 1) { in++; input[push.back()]++; } push.pop_back(); input[i] = in; } } alu.reset(); for(int i = 0; i < 14; i++) { alu.addInput(input[i]); } alu.performProgram(); if(alu.getRegisterValue(3) == 0) { return inputToInt(input); } return -1; } int main(int argc, char **argv) { if (argc < 2) { std::cerr << "You must provide input file!" << std::endl; return 1; } auto alu = getALU(argv[1]); std::cout << "The highest possible submarine number is \033[91;1m" << part1(alu) << "\033[0m." << std::endl; std::cout << "The smallest possible submarine number is \033[91;1m" << part2(alu) << "\033[0m." << std::endl; return 0; }