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..33f245d --- /dev/null +++ b/day17/src/main.rs @@ -0,0 +1,257 @@ +use std::collections::HashSet; +use std::collections::HashMap; +use std::env; +use std::fs; + +fn main() { + println!("Hello, AoC day 13!"); + + let args: Vec = env::args().collect(); + if args.len() != 2 { + println!("wrong number of arguments!"); + std::process::exit(1); + } + + let file_path = &args[1]; + + let binding = fs::read_to_string(file_path) + .expect("should be able to read the file"); + + let mut register: [u128; 3] = [0,0,0]; + let mut program: Vec = Vec::new(); + let mut instruction_pointer: usize = 0; + let mut stdout: Vec = Vec::new(); + + for line in binding.lines() { + if line.contains("Register A: ") { + register[0] = line.replace("Register A: ", "").parse().unwrap(); + } + if line.contains("Register B: ") { + register[1] = line.replace("Register B: ", "").parse().unwrap(); + } + if line.contains("Register B: ") { + register[2] = line.replace("Register B: ", "").parse().unwrap(); + } + + if line.contains("Program: ") { + program = line.replace("Program: ", "") + .split(",") + .map(|x| x.parse().unwrap()) + .collect(); + } + } + + println!("initial state:"); + print_state(®ister, &program, &instruction_pointer); + + // part a + print!("program output: "); + while let Ok(result) = step(&mut register, &program, &mut instruction_pointer) { + if let Some(output) = result { + stdout.push(output); + } + } + println!("{stdout:?}"); + + println!(""); + + // part b + let mut head = vec![0_u128]; + let mut suffixes: HashMap> = HashMap::new(); + let mut winners: HashSet = HashSet::new(); + while let Some(prefix) = head.pop() { + let desired; + if prefix == 0 { + desired = program[program.len()-1]; + } else { + let length = format!("{prefix:o}").len(); + if length >= program.len() { + winners.insert(prefix); + continue; + } + desired = program[program.len() - length - 1] + } + if suffixes.contains_key(&prefix) { continue; } + let octets = find_octets(prefix, desired); + for x in &octets { + head.push((prefix << 3) + (*x as u128)); + } + suffixes.insert(prefix, octets); + } + + let mut smallest = u128::MAX; + + for winner in winners { + if winner < smallest { + smallest = winner; + } + } + + println!("the smallest quining initial value was {smallest} "); + + register[0] = smallest; + register[1] = 0; + register[2] = 0; + + instruction_pointer = 0; + + stdout.drain(..); + + println!("program output: "); + while let Ok(result) = step(&mut register, &program, &mut instruction_pointer) { + if let Some(output) = result { + stdout.push(output); + } + } + println!("{program:?}"); + println!("{stdout:?}"); +} + +fn step( + register: &mut [u128; 3], + program: &Vec, + instruction_pointer: &mut usize) +-> Result, ()> { + + if *instruction_pointer >= program.len() { return Err(()); } + let instruction = program[*instruction_pointer]; + if *instruction_pointer + 1 >= program.len() { return Err(()); } + let operand = program[*instruction_pointer + 1]; + + let literal_operand = operand; + let combo_operand = match operand { + 4 => register[0], + 5 => register[1], + 6 => register[2], + anything => anything.into(), + }; + let mut output: Option = None; + + *instruction_pointer += 2; + + /* + println!("executing {} {}", + match instruction {0=>"adv",1=>"bxl",2=>"bst",3=>"jnz",4=>"bxc",5=>"out",6=>"bdv",7=>"cdv",_=>"???"}, + literal_operand); + */ + + match instruction { + 0 => { register[0] = div_thing(register[0], combo_operand); }, // adv + 1 => { register[1] = register[1] ^ (literal_operand as u128); }, // bxl + 2 => { register[1] = combo_operand % 8; }, // bst + 3 => match register[0] { // jnz + 0 => {}, + _ => { *instruction_pointer = literal_operand as usize; } + }, + 4 => { register[1] = register[1] ^ register[2]; }, // bxc + 5 => { output = Some((combo_operand % 8) as u8); }, // out + 6 => { register[1] = div_thing(register[0], combo_operand); }, // bdv + 7 => { register[2] = div_thing(register[0], combo_operand); }, // cdv + _ => { return Err(()); }, + } + return Ok(output); +} + +fn div_thing(a: u128, b: u128) -> u128 { + let power = 2_u128.pow(b.try_into().unwrap()); + return a / power; +} + +fn print_state(register: &[u128;3], program: &Vec, instruction_pointer: &usize) { + println!("registers: {register:?}"); + println!("program: {program:?}"); + println!("current pointer: {instruction_pointer}"); +} + +/* + + +2 bst -> modulo combo operand => B +4 bxc -> B xor C => B + +1 bxl -> B xor literal operand => B +4 bxc -> B xor C => B + +7 cdv -> A / 2^(combo operand) => C +5 out -> print combo operand + +4 bxc -> B xor C => B +1 bxl -> B xor literal operand => B + +1 bxl -> B xor literal operand => B +4 bxc -> B xor C => B + +5 out => print combo operand +5 out => print combo operand + +0 adv => A / 2^(combo operand) => A +3 jnz => jump literal operand if A != 0 + +3 jnz => jump literal operand if A != 0 +0 adv => A / 2^(combo operand) => A + +2,4,1,4,7,5,4,1,1,4,5,5,0,3,3,0 + +there is only one JNZ executed in the normal execution, and it goes to the start. +all instructions are always aligned. + + +2 bst -> A % 8 => B +1 bxl -> B xor 4 => B +7 cdv -> A / 2^B => C +4 bxc -> B xor C => B +1 bxl -> B xor 4 => B +5 out => print B +0 adv => A / 2^3 => A + +halt if A == 0, otherwise repeat + + +B = X % 8 +B = B xor 4 // flip byte 3 (4's position) +C = A >> B +B = B xor C +B = B xor 4 // flip byte 3 +print B +A = A / 2^3 +treat A as a sequence of octets + +0o7654321 +_111_110_101_100_011_010_001 +B = 001 +B = 001 xor 100 = 101 = 0d5 +C = _111_110_101_100_011_010_001 >> 5 = 001_111_101_011_000_110 +B = B xor C = 101 xor 001_111_101_011_000_110 = 001_111_101_011_000_011 +print 011 = 0d3 +A = A >> 3 + +last octet we want to output is 0 = 000 + +X = 000...ijk +B=ijk +B = i j k xor 4 => ~i j k +C = i j k >> ~i j k +B = ~i j k xor C +B = B xor 4 +print B + +*/ + +fn find_octets(prefix: u128, target: u8) -> Vec { + // returns all suffixes such that (prefix << 3) + suffix produces target as the output + let mut suffixes: Vec = Vec::with_capacity(3); + + for candidate in 0..8_u8 { + let a = (prefix << 3) + (candidate as u128); + let mut b = a % 8; + b = b ^ 4; + let c = a >> b; + b = b ^ c; + b = b ^ 4; + if b % 8 == target.into() { + suffixes.push(candidate); + } + } + + suffixes +} \ No newline at end of file