aoc2024: Implement day17 solution
This commit is contained in:
parent
869f4011a1
commit
a584e1a4ec
|
@ -15,6 +15,7 @@ set(AOC2024_PROJECTS
|
||||||
day14
|
day14
|
||||||
day15
|
day15
|
||||||
day16
|
day16
|
||||||
|
day17
|
||||||
full
|
full
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
set(SOURCES
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
add_executable(aoc2024_day17 ${SOURCES})
|
||||||
|
banan_include_headers(aoc2024_day17 ban)
|
||||||
|
banan_link_library(aoc2024_day17 libc)
|
||||||
|
|
||||||
|
install(TARGETS aoc2024_day17 OPTIONAL)
|
|
@ -0,0 +1,223 @@
|
||||||
|
#include <BAN/Vector.h>
|
||||||
|
#include <BAN/String.h>
|
||||||
|
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
using i8 = int8_t;
|
||||||
|
using i16 = int16_t;
|
||||||
|
using i32 = int32_t;
|
||||||
|
using i64 = int64_t;
|
||||||
|
using isize = ssize_t;
|
||||||
|
|
||||||
|
using u8 = uint8_t;
|
||||||
|
using u16 = uint16_t;
|
||||||
|
using u32 = uint32_t;
|
||||||
|
using u64 = uint64_t;
|
||||||
|
using usize = size_t;
|
||||||
|
|
||||||
|
struct Registers
|
||||||
|
{
|
||||||
|
u64 a, b, c;
|
||||||
|
usize ip;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum Opcodes
|
||||||
|
{
|
||||||
|
ADV = 0,
|
||||||
|
BXL = 1,
|
||||||
|
BST = 2,
|
||||||
|
JNZ = 3,
|
||||||
|
BXC = 4,
|
||||||
|
OUT = 5,
|
||||||
|
BDV = 6,
|
||||||
|
CDV = 7,
|
||||||
|
};
|
||||||
|
|
||||||
|
using Program = BAN::Vector<u8>;
|
||||||
|
|
||||||
|
struct ParseInputResult
|
||||||
|
{
|
||||||
|
Registers registers;
|
||||||
|
Program program;
|
||||||
|
};
|
||||||
|
|
||||||
|
static ParseInputResult parse_input(FILE* fp)
|
||||||
|
{
|
||||||
|
Registers registers;
|
||||||
|
ASSERT(fscanf(fp,
|
||||||
|
"Register A: %" SCNu64 "\n"
|
||||||
|
"Register B: %" SCNu64 "\n"
|
||||||
|
"Register C: %" SCNu64 "\n"
|
||||||
|
"\n"
|
||||||
|
"Program: ",
|
||||||
|
®isters.a, ®isters.b, ®isters.c
|
||||||
|
) == 3);
|
||||||
|
registers.ip = 0;
|
||||||
|
|
||||||
|
char buffer[128];
|
||||||
|
ASSERT(fgets(buffer, sizeof(buffer), fp));
|
||||||
|
|
||||||
|
Program program;
|
||||||
|
for (usize i = 0; buffer[i]; i++)
|
||||||
|
if (isdigit(buffer[i]))
|
||||||
|
MUST(program.push_back(buffer[i] - '0'));
|
||||||
|
|
||||||
|
return ParseInputResult {
|
||||||
|
.registers = BAN::move(registers),
|
||||||
|
.program = BAN::move(program)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::Vector<u64> emulate_program(Registers registers, const Program& program)
|
||||||
|
{
|
||||||
|
const auto combo =
|
||||||
|
[®isters](u8 combo) -> u64
|
||||||
|
{
|
||||||
|
switch (combo)
|
||||||
|
{
|
||||||
|
case 0: case 1: case 2: case 3: return combo;
|
||||||
|
case 4: return registers.a;
|
||||||
|
case 5: return registers.b;
|
||||||
|
case 6: return registers.c;
|
||||||
|
}
|
||||||
|
ASSERT_NOT_REACHED();
|
||||||
|
};
|
||||||
|
|
||||||
|
BAN::Vector<u64> output;
|
||||||
|
while (registers.ip < program.size())
|
||||||
|
{
|
||||||
|
const u8 opcode = program[registers.ip + 0];
|
||||||
|
const u8 operand = program[registers.ip + 1];
|
||||||
|
registers.ip += 2;
|
||||||
|
|
||||||
|
switch (opcode)
|
||||||
|
{
|
||||||
|
case Opcodes::ADV:
|
||||||
|
registers.a = registers.a >> combo(operand);
|
||||||
|
break;
|
||||||
|
case Opcodes::BXL:
|
||||||
|
registers.b = registers.b ^ operand;
|
||||||
|
break;
|
||||||
|
case Opcodes::BST:
|
||||||
|
registers.b = combo(operand) & 0x07;
|
||||||
|
break;
|
||||||
|
case Opcodes::JNZ:
|
||||||
|
if (registers.a != 0)
|
||||||
|
registers.ip = operand;
|
||||||
|
break;
|
||||||
|
case Opcodes::BXC:
|
||||||
|
registers.b = registers.b ^ registers.c;
|
||||||
|
break;
|
||||||
|
case Opcodes::OUT:
|
||||||
|
MUST(output.push_back(combo(operand) & 0x07));
|
||||||
|
break;
|
||||||
|
case Opcodes::BDV:
|
||||||
|
registers.b = registers.a >> combo(operand);
|
||||||
|
break;
|
||||||
|
case Opcodes::CDV:
|
||||||
|
registers.c = registers.a >> combo(operand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
BAN::String part1(FILE* fp)
|
||||||
|
{
|
||||||
|
auto [registers, program] = parse_input(fp);
|
||||||
|
auto output = emulate_program(registers, program);
|
||||||
|
|
||||||
|
BAN::String result;
|
||||||
|
MUST(result.resize(output.size() * 2 - 1));
|
||||||
|
|
||||||
|
for (usize i = 0; i < result.size(); i++)
|
||||||
|
result[i] = (i % 2) ? ',' : output[i / 2] + '0';
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static BAN::Optional<u64> recurse_part2(Registers initial_registers, u64 curr_a, usize curr_bits, usize output_done, const Program& program)
|
||||||
|
{
|
||||||
|
if (output_done >= program.size())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
BAN::Optional<u64> result;
|
||||||
|
for (u64 val = 0; val < 8; val++)
|
||||||
|
{
|
||||||
|
const u64 next_a = curr_a | (val << curr_bits);
|
||||||
|
|
||||||
|
auto registers = initial_registers;
|
||||||
|
registers.a = next_a;
|
||||||
|
|
||||||
|
auto output = emulate_program(registers, program);
|
||||||
|
if (output.size() < output_done + 1)
|
||||||
|
continue;
|
||||||
|
if (output.size() > program.size())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
bool match = true;
|
||||||
|
for (usize i = 0; i < output_done + 1 && match; i++)
|
||||||
|
if (output[i] != program[i])
|
||||||
|
match = false;
|
||||||
|
if (!match)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (output_done + 1 == program.size())
|
||||||
|
return next_a;
|
||||||
|
|
||||||
|
auto temp = recurse_part2(registers, next_a, curr_bits + 3, output_done + 1, program);
|
||||||
|
if (temp.has_value())
|
||||||
|
result = BAN::Math::min(result.value_or(BAN::numeric_limits<u64>::max()), temp.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
i64 part2(FILE* fp)
|
||||||
|
{
|
||||||
|
auto [initial_registers, program] = parse_input(fp);
|
||||||
|
|
||||||
|
BAN::Optional<u64> result;
|
||||||
|
for (u64 val = 0; val < 1024; val++)
|
||||||
|
{
|
||||||
|
auto registers = initial_registers;
|
||||||
|
registers.a = val;
|
||||||
|
|
||||||
|
auto output = emulate_program(registers, program);
|
||||||
|
if (output.empty() || output.size() > program.size())
|
||||||
|
continue;
|
||||||
|
if (output[0] != program[0])
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto temp = recurse_part2(registers, val, 10, 1, program);
|
||||||
|
if (temp.has_value())
|
||||||
|
result = BAN::Math::min(result.value_or(BAN::numeric_limits<u64>::max()), temp.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char** argv)
|
||||||
|
{
|
||||||
|
const char* file_path = "/usr/share/aoc2024/day17_input.txt";
|
||||||
|
|
||||||
|
if (argc >= 2)
|
||||||
|
file_path = argv[1];
|
||||||
|
|
||||||
|
FILE* fp = fopen(file_path, "r");
|
||||||
|
if (fp == nullptr)
|
||||||
|
{
|
||||||
|
perror("fopen");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("part1: %s\n", part1(fp).data());
|
||||||
|
|
||||||
|
fseek(fp, 0, SEEK_SET);
|
||||||
|
|
||||||
|
printf("part2: %" PRId64 "\n", part2(fp));
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
}
|
Loading…
Reference in New Issue