180 lines
4.8 KiB
C
180 lines
4.8 KiB
C
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#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];
|
|
}
|