day 17
This commit is contained in:
parent
8c5eef6a5b
commit
ea878a2067
|
|
@ -0,0 +1,5 @@
|
||||||
|
Register A: 61657405
|
||||||
|
Register B: 0
|
||||||
|
Register C: 0
|
||||||
|
|
||||||
|
Program: 2,4,1,2,7,5,4,3,0,3,1,7,5,5,3,0
|
||||||
|
|
@ -0,0 +1,242 @@
|
||||||
|
#include "aoc.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u64 a, b, c;
|
||||||
|
i64 ip;
|
||||||
|
} CPU;
|
||||||
|
|
||||||
|
static u64
|
||||||
|
parse_register(str line, Arena arena) {
|
||||||
|
str_find_result colon = str_find_left(line, STR(":"));
|
||||||
|
ASSERT(colon.found);
|
||||||
|
u64 result = (u64) parse_i64(str_trim(str_sub(line, colon.offset + 1, line.len)), arena);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static u64
|
||||||
|
get_combo(CPU *cpu, u64 operand) {
|
||||||
|
if (operand <= 3) return operand;
|
||||||
|
else if (operand == 4) return cpu->a;
|
||||||
|
else if (operand == 5) return cpu->b;
|
||||||
|
else if (operand == 6) return cpu->c;
|
||||||
|
else NOT_REACHABLE();
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef DYNAMIC_ARRAY(u8) Program;
|
||||||
|
|
||||||
|
static void
|
||||||
|
run(Arena *arena, CPU * restrict cpu, Program program, Program *output) {
|
||||||
|
output->len = 0;
|
||||||
|
while (cpu->ip >= 0 && cpu->ip < program.len) {
|
||||||
|
ASSERT(cpu->ip % 2 == 0);
|
||||||
|
u8 op = program.data[cpu->ip];
|
||||||
|
ASSERT(op >= 0);
|
||||||
|
ASSERT(op < 8);
|
||||||
|
u8 arg = program.data[cpu->ip + 1];
|
||||||
|
|
||||||
|
bool took_branch = false;
|
||||||
|
if (op == 0) {
|
||||||
|
// adv
|
||||||
|
cpu->a = cpu->a / (1ull << get_combo(cpu, (u64) arg));
|
||||||
|
}
|
||||||
|
else if (op == 1) {
|
||||||
|
// bxl
|
||||||
|
cpu->b = cpu->b ^ (u64) arg;
|
||||||
|
}
|
||||||
|
else if (op == 2) {
|
||||||
|
// bst
|
||||||
|
cpu->b = get_combo(cpu, (u64) arg) % 8;
|
||||||
|
}
|
||||||
|
else if (op == 3) {
|
||||||
|
// jnz
|
||||||
|
if (cpu->a != 0) {
|
||||||
|
cpu->ip = (u64) arg;
|
||||||
|
took_branch = true;
|
||||||
|
}
|
||||||
|
cpu->b = 0;
|
||||||
|
cpu->c = 0;
|
||||||
|
}
|
||||||
|
else if (op == 4) {
|
||||||
|
// bxc
|
||||||
|
cpu->b ^= cpu->c;
|
||||||
|
}
|
||||||
|
else if (op == 5) {
|
||||||
|
// out
|
||||||
|
u8 value = get_combo(cpu, (u64) arg) % 8;
|
||||||
|
*push(output, arena) = value;
|
||||||
|
}
|
||||||
|
else if (op == 6) {
|
||||||
|
// bdv
|
||||||
|
cpu->b = cpu->a / (1ull << get_combo(cpu, (u64) arg));
|
||||||
|
}
|
||||||
|
else if (op == 7) {
|
||||||
|
// cdv
|
||||||
|
cpu->c = cpu->a / (1ull << get_combo(cpu, (u64) arg));
|
||||||
|
}
|
||||||
|
else NOT_REACHABLE();
|
||||||
|
|
||||||
|
if (!took_branch) {
|
||||||
|
cpu->ip += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_program(Program program) {
|
||||||
|
for (int i = 0; i < program.len; i++) {
|
||||||
|
printf("%d", program.data[i]);
|
||||||
|
if (i < program.len - 1) {
|
||||||
|
putchar(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
puts("");
|
||||||
|
}
|
||||||
|
|
||||||
|
static u8
|
||||||
|
process(u64 *a) {
|
||||||
|
u64 b, c;
|
||||||
|
b = *a % 8;
|
||||||
|
b ^= 2;
|
||||||
|
c = *a >> b;
|
||||||
|
b ^= c;
|
||||||
|
*a >>= 3;
|
||||||
|
b ^= 7;
|
||||||
|
return b % 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check(Program *target, u64 a) {
|
||||||
|
isize index = 0;
|
||||||
|
for (;index < target->len; index++) {
|
||||||
|
if (process(&a) != target->data[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index == target->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
check_suffix(Program *target, u64 a, i32 suffix_len) {
|
||||||
|
isize index = 0;
|
||||||
|
for (; index < target->len - suffix_len; index++) {
|
||||||
|
process(&a);
|
||||||
|
}
|
||||||
|
for (; index < target->len; index++) {
|
||||||
|
if (process(&a) != target->data[index]) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return index == target->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
static i64
|
||||||
|
solve(Program *target, i32 index, u64 a) {
|
||||||
|
if (index == -1) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (u64 i = 0; i < 8; i++) {
|
||||||
|
u64 new_a = a;
|
||||||
|
new_a |= (i << (3 * index));
|
||||||
|
if (check_suffix(target, new_a, target->len - index)) {
|
||||||
|
i64 result;
|
||||||
|
if ((result = solve(target, index - 1, new_a)) != -1) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
Arena *arena = make_arena(Megabytes(1));
|
||||||
|
Tokens lines = read_lines(arena, argv[1]);
|
||||||
|
|
||||||
|
CPU cpu = {0};
|
||||||
|
cpu.a = parse_register(lines.tokens[0], *arena);
|
||||||
|
cpu.b = parse_register(lines.tokens[1], *arena);
|
||||||
|
cpu.c = parse_register(lines.tokens[2], *arena);
|
||||||
|
|
||||||
|
CPU initial = cpu;
|
||||||
|
|
||||||
|
Program program = {0};
|
||||||
|
str token;
|
||||||
|
str inp = lines.tokens[lines.len - 1];
|
||||||
|
str_find_result colon = str_find_left(inp, STR(":"));
|
||||||
|
inp = str_trim(str_sub(inp, colon.offset + 1, inp.len));
|
||||||
|
while ((token = str_next_token(&inp, STR(","))).len > 0) {
|
||||||
|
token = str_trim(token);
|
||||||
|
*push(&program, arena) = (u8) parse_i64(token, *arena);
|
||||||
|
}
|
||||||
|
/* dump_program(program); */
|
||||||
|
|
||||||
|
for (i32 i = 0; i < program.len; i += 2) {
|
||||||
|
u8 op = program.data[i];
|
||||||
|
u8 arg = program.data[i + 1];
|
||||||
|
char combo[16] = {0};
|
||||||
|
if (arg <= 3) {
|
||||||
|
snprintf(combo, sizeof(combo), "%d", arg);
|
||||||
|
} else if (arg == 4) {
|
||||||
|
snprintf(combo, sizeof(combo), "A");
|
||||||
|
} else if (arg == 5) {
|
||||||
|
snprintf(combo, sizeof(combo), "B");
|
||||||
|
} else if (arg == 6) {
|
||||||
|
snprintf(combo, sizeof(combo), "C");
|
||||||
|
}
|
||||||
|
switch (op) {
|
||||||
|
case 0: printf("A = A / (1 << %s)\n", combo); break;
|
||||||
|
case 1: printf("B = B ^ %d\n", arg); break;
|
||||||
|
case 2: printf("B = %s %% 8\n", combo); break;
|
||||||
|
case 3: printf("JNZ A, %d\n", arg); break;
|
||||||
|
case 4: printf("B = B ^ C\n"); break;
|
||||||
|
case 5: printf("OUT %s %% 8\n", combo); break;
|
||||||
|
case 6: printf("B = A / (1 << %s)\n", combo); break;
|
||||||
|
case 7: printf("C = A / (1 << %s)\n", combo); break;
|
||||||
|
default: NOT_REACHABLE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Program output = {0};
|
||||||
|
run(arena, &cpu, program, &output);
|
||||||
|
dump_program(output);
|
||||||
|
|
||||||
|
// Part 2
|
||||||
|
/**
|
||||||
|
* Our input program in disassembled form:
|
||||||
|
* B = A % 8 ; equivalent to B = A & 0b111
|
||||||
|
* B = B ^ 2 ; flips the 2nd bit of B
|
||||||
|
* C = A / (1 << B) ; equivalent to C = A >> B
|
||||||
|
* B = B ^ C ; flips the bits of B that are set in C
|
||||||
|
* A = A / (1 << 3) ; equivalent to A = A >> 3
|
||||||
|
* B = B ^ 7 ; flips lower 3 bits of B
|
||||||
|
* OUT B % 8 ; outputs lower 3 bits of B
|
||||||
|
* JNZ A, 0
|
||||||
|
*
|
||||||
|
* Therefore, our input program processes A in chunks of 3 bits at a time,
|
||||||
|
* until A becomes 0.
|
||||||
|
* Registers B and C do not retain state between iterations of the program.
|
||||||
|
* Hence, the length of the program's output is the number of octals in A.
|
||||||
|
*
|
||||||
|
* Combined:
|
||||||
|
* B1 = A % 8
|
||||||
|
* B2 = B1 ^ 2
|
||||||
|
* C1 = A >> B2
|
||||||
|
* B3 = B2 ^ C1
|
||||||
|
* A2 = A >> 3
|
||||||
|
* B4 = B3 ^ 7
|
||||||
|
* OUT (B4 % 8)
|
||||||
|
* JNZ A2, 0
|
||||||
|
*
|
||||||
|
* OUT (B4 % 8) = OUT ((B3 ^ 7) % 8)
|
||||||
|
* = OUT (((B2 ^ C1) ^ 7) % 8)
|
||||||
|
* = OUT (((B2 ^ (A >> B2)) ^ 7) % 8)
|
||||||
|
* = OUT (((((A % 8) ^ 2) ^ (A >> ((A % 8) ^ 2))) ^ 7) % 8)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
i64 part_2 = solve(&program, program.len - 1, 0);
|
||||||
|
ASSERT(part_2 != -1);
|
||||||
|
printf("%ld\n", part_2);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
Register A: 729
|
||||||
|
Register B: 0
|
||||||
|
Register C: 0
|
||||||
|
|
||||||
|
Program: 0,1,5,4,3,0
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
Register A: 2024
|
||||||
|
Register B: 0
|
||||||
|
Register C: 0
|
||||||
|
|
||||||
|
Program: 0,3,5,4,3,0
|
||||||
Loading…
Reference in New Issue