day 17 was a doozy
This commit is contained in:
		
							parent
							
								
									534ebef096
								
							
						
					
					
						commit
						5f0d6f9869
					
				
							
								
								
									
										6
									
								
								day17/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								day17/Cargo.toml
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | [package] | ||||||
|  | name = "day17" | ||||||
|  | version = "0.1.0" | ||||||
|  | edition = "2021" | ||||||
|  | 
 | ||||||
|  | [dependencies] | ||||||
							
								
								
									
										257
									
								
								day17/src/main.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										257
									
								
								day17/src/main.rs
									
									
									
									
									
										Normal file
									
								
							| @ -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<String> = 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<u8> = Vec::new(); | ||||||
|  |     let mut instruction_pointer: usize = 0; | ||||||
|  |     let mut stdout: Vec<u8> = 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<u128, Vec<u8>> = HashMap::new(); | ||||||
|  |     let mut winners: HashSet<u128> = 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<u8>, 
 | ||||||
|  |     instruction_pointer: &mut usize) 
 | ||||||
|  | -> Result<Option<u8>, ()> { | ||||||
|  | 
 | ||||||
|  |     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<u8> = 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<u8>, 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<u8> {  
 | ||||||
|  |     // returns all suffixes such that (prefix << 3) + suffix produces target as the output
 | ||||||
|  |     let mut suffixes: Vec<u8> = 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 | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user