#include #include #include #include #define CHARS_MAX 128 #define MAX_INSTRUCTIONS 128 #define BIT_JUMPED 0 enum { A, B, C, REGISTERS_LEN }; enum { ADV, BXL, BST, JNZ, BXC, OUT, BDV, CDV }; void execute(uint64_t registers[REGISTERS_LEN], uint8_t *instructions, uint32_t *pc, uint8_t *status, char *output_buffer, int *buf_len); uint64_t combo(uint64_t registers[REGISTERS_LEN], uint8_t operand); uint64_t revert(uint8_t *instructions_inverted, uint8_t *instructions, uint32_t instruction_count, uint64_t current_A, uint32_t i, int print_register); int main() { char *p, *buf, c; buf = calloc(CHARS_MAX, sizeof(char)); p = buf; uint64_t registers[3]; int reading_register = 0, operand = 0; uint8_t *instructions = calloc(MAX_INSTRUCTIONS, sizeof(instructions[0])); uint32_t instruction_count = 0; while ((c = getchar()) != EOF) { if (reading_register < 3) { *p++ = c; if (c != '\n') { continue; } p = buf; while(*p++ != ':'); p++; sscanf(p, "%li", ®isters[reading_register++]); memset(buf, 0, CHARS_MAX); p = buf; } else { if (c < 48 || c > 57) continue; uint8_t num = c - 48; instructions[instruction_count++] = num; } } uint32_t pc = 0; uint8_t status = 0; int buf_len = 0; while (pc < instruction_count) { execute(registers, instructions, &pc, &status, buf, &buf_len); if (!(status & (1 << BIT_JUMPED))) { pc += 2; } } buf[buf_len - 1] = '\0'; printf("%s\n", buf); // Find print register int print_register = A; for (uint32_t i = 0; i < instruction_count; i+=2) { if (instructions[i] == OUT) { print_register = instructions[i+1] - 4; break; } } // Invert instruction list uint8_t *instructions_inverted = calloc(instruction_count, sizeof(instructions[0])); for (uint32_t i = 0; i < instruction_count; i++) { instructions_inverted[i] = instructions[instruction_count - i - 1]; } printf("%lu\n", revert(instructions_inverted, instructions, instruction_count, 0, 0, print_register)); free(buf); free(instructions); free(instructions_inverted); } uint64_t revert(uint8_t *instructions_inverted, uint8_t *instructions, uint32_t instruction_count, uint64_t current_A, uint32_t i, int print_register) { if (i == instruction_count) { return current_A >> 3; } // Try each 3-bit LSB of A for (uint32_t j = 0; j < 8; j++) { uint64_t next_A = current_A + j; uint64_t registers_copy[3] = {next_A, 0, 0}; uint32_t pc = 0; uint8_t status = 0; for (pc = 0; pc < instruction_count - 4; pc+=2) { execute(registers_copy, instructions, &pc, &status, NULL, NULL); } // Check print register uint64_t expected_output = instructions_inverted[i]; if (registers_copy[print_register] % 8 == expected_output) { uint64_t a = revert(instructions_inverted, instructions, instruction_count, next_A << 3, i + 1, print_register); if (a) { return a; } } } return 0; } void execute(uint64_t registers[REGISTERS_LEN], uint8_t *instructions, uint32_t *pc, uint8_t *status, char *output_buffer, int *buf_len) { uint8_t opcode = instructions[*pc]; uint8_t operand = instructions[(*pc) + 1]; uint8_t current_status = *status; uint8_t next_status = 0; switch (opcode) { case ADV: registers[A] = registers[A] / (1 << combo(registers, operand)); break; case BXL: registers[B] = registers[B] ^ operand; break; case BST: registers[B] = combo(registers, operand) % 8; break; case JNZ: if (registers[A] != 0) { *pc = operand; next_status |= 1 << BIT_JUMPED; } break; case BXC: registers[B] = registers[B] ^ registers[C]; break; case OUT: output_buffer[(*buf_len)++] = ((uint8_t)combo(registers, operand) & 7) + 48; output_buffer[(*buf_len)++] = ','; break; case BDV: registers[B] = registers[A] / combo(registers, operand); break; case CDV: registers[C] = registers[A] / (1 << combo(registers, operand)); break; } *status = next_status; } uint64_t combo(uint64_t registers[REGISTERS_LEN], uint8_t operand) { if (operand < 4) { return operand; } if (operand == 7) { printf("INVALID PROGRAM\n"); return 0; } return registers[operand - 4]; }