Files
adventofcode2024/day17/c/day17.c

180 lines
4.8 KiB
C
Raw Normal View History

2024-12-17 23:48:59 +01:00
#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", &registers[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];
}