use std::collections::HashMap; static INPUT: &[u8] = include_bytes!("input.txt"); #[derive(Debug, Copy, Clone)] enum WireOrLiteral { Wire([u8; 2]), Literal(u16) } #[derive(Debug, Copy, Clone)] enum Connection { Direct(WireOrLiteral), Not (WireOrLiteral), And (WireOrLiteral, WireOrLiteral), Or (WireOrLiteral, WireOrLiteral), LShift(WireOrLiteral, WireOrLiteral), RShift(WireOrLiteral, WireOrLiteral), } #[derive(Debug, Copy, Clone)] enum Cache { Cached(u16), Uncached(Connection) } fn main() { let valueified = INPUT .split(|b| *b == b'\n').filter(|b| !b.is_empty()) .map(|line| { let mut bits = line.split(|b| *b == b'-'); let (rhs, lhs) = (bits.next().unwrap().strip_suffix(b" ").unwrap(), bits.next().unwrap().strip_prefix(b"> ").unwrap()); let mut cmdbits = rhs.split(|b| *b == b' '); let cmdbits = (cmdbits.next().unwrap(), cmdbits.next().unwrap_or(&[]), cmdbits.next().unwrap_or(&[])); let connection = match cmdbits { (wol, &[], &[]) => Connection::Direct(parse_wol(wol)), (b"NOT", wol, &[]) => Connection::Not(parse_wol(wol)), (wol1, b"AND", wol2) => Connection::And(parse_wol(wol1), parse_wol(wol2)), (wol1, b"OR", wol2) => Connection::Or(parse_wol(wol1), parse_wol(wol2)), (wol1, b"LSHIFT", wol2) => Connection::LShift(parse_wol(wol1), parse_wol(wol2)), (wol1, b"RSHIFT", wol2) => Connection::RShift(parse_wol(wol1), parse_wol(wol2)), _ => unreachable!() }; let wire = [lhs[0], *lhs.get(1).unwrap_or(&0)]; (wire, Cache::Uncached(connection)) }) .collect::>(); let mut part1 = valueified.clone(); let a_val = walk(*b"a\0", &mut part1); println!("a: {a_val}"); let mut part2 = valueified.clone(); part2.insert(*b"b\0", Cache::Uncached(Connection::Direct(WireOrLiteral::Literal(a_val)))); let a_val_2 = walk(*b"a\0", &mut part2); println!("a with b={a_val}: {a_val_2}"); } fn parse_wol(bytes: &[u8]) -> WireOrLiteral { if bytes.iter().all(u8::is_ascii_digit) { WireOrLiteral::Literal(std::str::from_utf8(bytes).unwrap().parse().unwrap()) } else { WireOrLiteral::Wire([bytes[0], *bytes.get(1).unwrap_or(&0)]) } } fn walk(key: [u8; 2], map: &mut HashMap<[u8; 2], Cache>) -> u16 { match map[&key] { Cache::Cached(val) => val, Cache::Uncached(conn) => { let val = match conn { Connection::Direct(wol) => get_wol(wol, map), Connection::Not(wol) => !get_wol(wol, map), Connection::And(wol1, wol2) => get_wol(wol1, map) & get_wol(wol2, map), Connection::Or(wol1, wol2) => get_wol(wol1, map) | get_wol(wol2, map), Connection::LShift(wol1, wol2) => get_wol(wol1, map) << get_wol(wol2, map), Connection::RShift(wol1, wol2) => get_wol(wol1, map) >> get_wol(wol2, map) }; map.insert(key, Cache::Cached(val)); val } } } fn get_wol(wol: WireOrLiteral, map: &mut HashMap<[u8; 2], Cache>) -> u16 { match wol { WireOrLiteral::Wire(wire) => walk(wire, map), WireOrLiteral::Literal(literal) => literal } }