use itertools::Itertools; use std::collections::HashMap; pub fn day24() { let (_values, mut connections) = input(&crate::input(24)); fn swap(connections: &mut Vec, a_name: &str, b_name: &str) { let a = connections .iter() .position(|connection| &connection.output == a_name) .unwrap(); let b = connections .iter() .position(|connection| &connection.output == b_name) .unwrap(); connections[a].output = b_name.to_string(); connections[b].output = a_name.to_string(); } swap(&mut connections, "z06", "vwr"); swap(&mut connections, "z11", "tqm"); swap(&mut connections, "z16", "kfs"); swap(&mut connections, "hcm", "gfv"); fn wire_number(w: &str) -> u64 { w.get(1..).unwrap().parse().unwrap() } fn is_start_wire(w: &str) -> bool { w.starts_with("x") || w.starts_with("y") } fn are_start_wires(a: &str, b: &str) -> Option { if !is_start_wire(a) || !is_start_wire(b) { return None; } let a_n = wire_number(a); let b_n = wire_number(b); if a_n == b_n { Some(a_n) } else { None } } let mut carry_out_wires: HashMap = HashMap::new(); let mut second_carries: HashMap = HashMap::new(); for i in 0..45 { // The first sum wire goes xi XOR yi let first_sum = connections.iter().find(|connection| { are_start_wires(&connection.a, &connection.b) == Some(i) && connection.f == xor }); let first_sum = match first_sum { Some(x) => &x.output, None => panic!("First sum for {i} does not exist."), }; // The first carry wire goes xi AND yi let first_carry = connections.iter().find(|connection| { are_start_wires(&connection.a, &connection.b) == Some(i) && connection.f == and }); let first_carry = match first_carry { Some(x) => &x.output, None => panic!("First carry for {i} does not exist."), }; // If this is 0, the first carry is the carry_out and the first sum is the output (and skip the rest) if i == 0 { if wire_number(&first_sum) != i || !first_sum.starts_with("z") { panic!("The first output wire is wrong."); } carry_out_wires.insert(0, first_carry.clone()); continue; } // The output wire goes [first_sum] XOR [prev carry_out] -> zi let prev_carry_out = carry_out_wires.get(&(i - 1)).unwrap(); let output = &connections .iter() .find(|connection| { let p1 = &connection.a == first_sum && &connection.b == prev_carry_out && connection.f == xor; let p2 = &connection.b == first_sum && &connection.a == prev_carry_out && connection.f == xor; p1 || p2 }) .expect(&format!("Couldn't find an output for {i}. Previous carry-out was {prev_carry_out} and first_sum was {first_sum}.")) .output; if !output.starts_with("z") || wire_number(&output) != i { println!( "The output for {i} should be z{i} but is instead {}", output ); } // The second carry wire goes [first_sum] AND [prev carry_out] let second_carry = &connections .iter() .find(|connection| { let p1 = &connection.a == first_sum && &connection.b == prev_carry_out && connection.f == and; let p2 = &connection.b == first_sum && &connection.a == prev_carry_out && connection.f == and; p1 || p2 }) .expect(&format!("Couldn't find a second carry for {i}.")) .output; second_carries.insert(i, second_carry.clone()); // The carry_out goes [first_carry OR second_carry] let carry_out = &connections .iter() .find(|connection| { let p1 = &connection.a == first_carry && &connection.b == second_carry && connection.f == or; let p2 = &connection.b == first_carry && &connection.a == second_carry && connection.f == or; p1 || p2 }) .expect(&format!("Couldn't find a carry out for {i}.")) .output; carry_out_wires.insert(i, carry_out.clone()); } // Finally, make sure z45 is carry_out of 44 if carry_out_wires.get(&44).unwrap() != "z45" { println!( "The final carry out should be z45 but is instead {}", carry_out_wires.get(&44).unwrap() ); } } fn run_system_with_inputs(connections: &Vec, x: u64, y: u64) -> u64 { let mut values: HashMap = HashMap::new(); for b in 0..45 { values.insert(format!("x{:02}", b), ((1 << b) & x) != 0); values.insert(format!("y{:02}", b), ((1 << b) & y) != 0); } run_system(&values, connections) } fn run_system(values: &HashMap, connections: &Vec) -> u64 { let mut values = values.clone(); let mut connections = connections.clone(); let mut remaining_connections = vec![]; while !connections.is_empty() { for connection in connections { if values.contains_key(&connection.a) && values.contains_key(&connection.b) { values.insert( connection.output.to_string(), (connection.f)(values[&connection.a], values[&connection.b]), ); } else { remaining_connections.push(connection); } } connections = remaining_connections; remaining_connections = vec![]; } let mut number: u64 = 0; for (wire, value) in values.iter() { if wire.starts_with("z") { let place: u64 = wire.get(1..).unwrap().parse().unwrap(); number = number | ((*value as u64) << place); } } number } type Gate = fn(bool, bool) -> bool; #[derive(Clone)] struct Connection { a: String, b: String, f: Gate, output: String, } impl Connection { fn new(a: String, b: String, f: Gate, output: String) -> Self { Self { a, b, f, output } } } fn input(s: &str) -> (HashMap, Vec) { let (values_input, gates_input) = s.split("\n\n").collect_tuple().unwrap(); let mut values: HashMap = values_input .lines() .map(|line| { let (name, value) = line.split(": ").collect_tuple().unwrap(); let value = match value { "0" => false, "1" => true, _ => panic!(), }; (name.to_string(), value) }) .collect(); let mut connections: Vec = gates_input .lines() .map(|line| { let parts: Vec<&str> = line.split(" ").collect(); let (a, gate, b, c) = (parts[0], parts[1], parts[2], parts[4]); let gate = match gate { "AND" => and, "OR" => or, "XOR" => xor, _ => panic!(), }; Connection::new(a.to_string(), b.to_string(), gate, c.to_string()) }) .collect(); (values, connections) } fn and(a: bool, b: bool) -> bool { a && b } fn or(a: bool, b: bool) -> bool { a || b } fn xor(a: bool, b: bool) -> bool { a ^ b }