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