diff --git a/.gitea/workflows/build_and_run.yml b/.gitea/workflows/build_and_run.yml index 4bef9ff..1670e95 100644 --- a/.gitea/workflows/build_and_run.yml +++ b/.gitea/workflows/build_and_run.yml @@ -11,7 +11,7 @@ jobs: name: Challenge for day strategy: matrix: - day_number: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + day_number: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] runs-on: rust-bookworm steps: - name: Check out repository code diff --git a/day17/Cargo.toml b/day17/Cargo.toml new file mode 100644 index 0000000..7d3f4fa --- /dev/null +++ b/day17/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day17" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/day17/src/main.rs b/day17/src/main.rs new file mode 100644 index 0000000..2ad9759 --- /dev/null +++ b/day17/src/main.rs @@ -0,0 +1,132 @@ +use std::collections::HashSet; +use std::{env, fs}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, PartialOrd)] +enum Instruction { + Adv(u64), // Division A = A / 2^x + Bdv(u64), // Division B = A / 2^x + Cdv(u64), // Division C = A / 2^x + Bst(u64), // Combo % 8 B = x % 8 + Jnz(u64), // Jump if not 0. if A != 0 { jump to x } + Out(u64), // Print x % 8 echo(x % 8) + Bxl(u64), // Bitwise XOR B = B XOR x + Bxc, // Bitwise XOR B = B XOR C +} + +fn read_input(path: &str) -> String { + fs::read_to_string(path).expect("Cannot read file.") +} + +fn parse_input(input: &str) -> (Vec, Vec, u64, u64, u64) { + let mut instructions = Vec::new(); + + let mut lines = input.lines(); + let a = lines.next().unwrap()[12..].parse::().unwrap(); + let b = lines.next().unwrap()[12..].parse::().unwrap(); + let c = lines.next().unwrap()[12..].parse::().unwrap(); + + lines.next(); + let raw_program = lines.next().unwrap()[9..] + .split(',') + .map(|s| s.to_string()) + .collect::>(); + for a in raw_program.chunks(2) { + let operand = a[1].parse().unwrap(); + instructions.push(match a[0].as_str() { + "0" => Instruction::Adv(operand), + "1" => Instruction::Bxl(operand), + "2" => Instruction::Bst(operand), + "3" => Instruction::Jnz(operand), + "4" => Instruction::Bxc, + "5" => Instruction::Out(operand), + "6" => Instruction::Bdv(operand), + "7" => Instruction::Cdv(operand), + _ => unreachable!(), + }) + } + + (instructions, raw_program, a, b, c) +} + +fn get_value_for_operand(operand: u64, a: u64, b: u64, c: u64) -> u64 { + match operand { + 0..=3 => operand, + 4 => a, + 5 => b, + 6 => c, + _ => unreachable!(), + } +} + +fn run_program(instructions: &Vec, mut a: u64, mut b: u64, mut c: u64) -> Vec { + let mut output = Vec::new(); + let mut pc = 0; + while pc < instructions.len() { + let instruction = instructions[pc]; + match instruction { + Instruction::Adv(x) => a = a / 2_u64.pow(get_value_for_operand(x, a, b, c) as u32), + Instruction::Bdv(x) => b = a / 2_u64.pow(get_value_for_operand(x, a, b, c) as u32), + Instruction::Cdv(x) => c = a / 2_u64.pow(get_value_for_operand(x, a, b, c) as u32), + Instruction::Bst(x) => b = get_value_for_operand(x, a, b, c) % 8, + Instruction::Jnz(x) => { + if a != 0 { + pc = x as usize + } + } + Instruction::Out(x) => output.push((get_value_for_operand(x, a, b, c) % 8).to_string()), + Instruction::Bxl(x) => b = b ^ x, + Instruction::Bxc => b = b ^ c, + } + match instruction { + Instruction::Jnz(_) => { + if a == 0 { + pc += 1 + } + } + _ => pc += 1, + } + } + output +} + +fn best_quine(instruction: &Vec, raw_program: &Vec) -> u64 { + let mut valid = HashSet::new(); + + let mut seen = HashSet::new(); + let mut queue = vec![0]; + while let Some(mut possibility) = queue.pop() { + possibility <<= 3; + for x in possibility..(possibility + 8) { + let output = run_program(instruction, x, 0, 0); + if !output.is_empty() + && *output.first().unwrap() == raw_program[raw_program.len() - output.len()] + { + if raw_program.len() == output.len() { + valid.insert(x); + } else if !seen.contains(&x) { + seen.insert(x); + queue.push(x); + } + } + } + } + + *valid.iter().min().unwrap_or(&0) +} + +fn main() { + let args: Vec = env::args().collect(); + for arg in args.iter().skip(1) { + let input = read_input(&arg); + let (instructions, raw_program, a, b, c) = parse_input(&input); + println!("[{}]", &arg); + println!( + "\t[Part 1] => Answer is '{}'.", + run_program(&instructions, a, b, c).join(",") + ); + println!( + "\t[Part 2] => Answer is '{}'.", + best_quine(&instructions, &raw_program) + ); + } +}