use std::collections::HashMap; #[path = "../lib.rs"] mod lib; enum Operator { And, Or, Xor, } #[derive(PartialEq, Eq, PartialOrd, Ord)] enum NodeType { X, Y, Z, XandY, XandY0, XxorY, CarryAndXY, Carry, } fn parse_input() -> (usize, usize, HashMap) { let mut rules = HashMap::new(); let mut x = 0; let mut y = 0; let mut init = true; for line in lib::iter_input() { if line == "" { init = false; } else if init { let (key, s) = line.split_once(": ").unwrap(); if s == "1" { let k = key[1..].parse::().unwrap(); if key.starts_with('x') { x |= 1 << k; } else { y |= 1 << k; } } } else { let (tmp1, key) = line.split_once(" -> ").unwrap(); let (lhs, tmp2) = tmp1.split_once(' ').unwrap(); let (sop, rhs) = tmp2.split_once(' ').unwrap(); let op = match sop { "AND" => Operator::And, "OR" => Operator::Or, "XOR" => Operator::Xor, _ => unreachable!(), }; rules.insert(key.to_string(), (lhs.to_string(), op, rhs.to_string())); } } return (x, y, rules); } fn get_value( key: &str, x: usize, y: usize, values: &mut HashMap, rules: &HashMap, ) -> bool { if let Some(r) = values.get(key) { return *r; } else if key.starts_with('x') { let k = key[1..].parse::().unwrap(); return (x >> k) & 1 == 1; } else if key.starts_with('y') { let k = key[1..].parse::().unwrap(); return (y >> k) & 1 == 1; } let (lhs, op, rhs) = rules.get(key).unwrap(); let l = get_value(lhs, x, y, values, rules); let r = get_value(rhs, x, y, values, rules); return match op { Operator::And => l && r, Operator::Or => l || r, Operator::Xor => l ^ r, }; } fn calc(x: usize, y: usize, rules: &HashMap) -> usize { let mut values = HashMap::new(); let mut z = 0; for key in rules.keys() { if key.starts_with('z') && get_value(key, x, y, &mut values, &rules) { z |= 1 << key[1..].parse::().unwrap(); } } return z; } fn rules2dot(rules: &HashMap) { println!("digraph d {{"); for (key, (lhs, op, rhs)) in rules.iter() { let color = match op { Operator::And => "red", Operator::Or => "green", Operator::Xor => "yellow", }; println!(" {lhs} -> {key};"); println!(" {rhs} -> {key};"); println!(" {key} [fillcolor={color},style=filled];"); } println!("}}"); } fn get_type(key: &str, rules: &HashMap) -> NodeType { if key.starts_with('x') { return NodeType::X; } else if key.starts_with('y') { return NodeType::Y; } let (lhs, op, rhs) = rules.get(key).unwrap(); // println!("{key}: {lhs} {op:?} {rhs}"); if lhs.starts_with('x') || rhs.starts_with('x') { return match op { Operator::And => if &lhs[1..] == "00" { NodeType::XandY0 } else { NodeType::XandY }, Operator::Or => unreachable!(), Operator::Xor => NodeType::XxorY, }; } return match op { Operator::And => NodeType::CarryAndXY, Operator::Or => NodeType::Carry, Operator::Xor => NodeType::Z, }; } fn get_errors(rules: &HashMap) -> String { // addition with logic gates works like this: // // z(i) = x(i) ^ y(i) ^ carry(i) // carry(i + 1) = (x(i) && y(i)) || (carry(i) && (x(i) ^ y(i))) // // Each node can be identified by looking at the inputs and operator only. // The first and last bits are special. The first bit has no carry; // the last bit has only carry. let mut errors = vec![]; let mut outputs = HashMap::new(); for (key, (lhs, _, rhs)) in rules.iter() { outputs.entry(key).or_insert(vec![]); outputs.entry(lhs).or_insert(vec![]).push(key); outputs.entry(rhs).or_insert(vec![]).push(key); } for (key, out) in outputs.iter() { let mut out_types = vec![]; for k in out.iter() { out_types.push(get_type(k, &rules)); } out_types.sort(); if !match (get_type(key, &rules), &out_types[..]) { (NodeType::X, [NodeType::XandY, NodeType::XxorY]) => true, (NodeType::Y, [NodeType::XandY, NodeType::XxorY]) => true, (NodeType::X, [NodeType::XandY0, NodeType::XxorY]) => true, (NodeType::Y, [NodeType::XandY0, NodeType::XxorY]) => true, (NodeType::Z, []) => true, (NodeType::XandY, [NodeType::Carry]) => true, (NodeType::XandY0, [NodeType::Z, NodeType::CarryAndXY]) => true, (NodeType::XxorY, [NodeType::Z, NodeType::CarryAndXY]) => true, (NodeType::XxorY, []) if *key == "z00" => true, (NodeType::CarryAndXY, [NodeType::Carry]) => true, (NodeType::Carry, [NodeType::Z, NodeType::CarryAndXY]) => true, (NodeType::Carry, []) if *key == "z45" => true, _ => false, } { errors.push(key.to_string()); } } errors.sort(); return errors.join(","); } fn main() { let (x, y, rules) = parse_input(); println!("part1: {}", calc(x, y, &rules)); println!("part2: {}", get_errors(&rules)); }