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