forked from Bananymous/banan-os
				
			
		
			
				
	
	
		
			224 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C++
		
	
	
	
| #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);
 | |
| }
 |