From 00000150f0a21e42a2dc16b26c5da0c1794d9661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Jane=C5=BEi=C4=8D?= Date: Sun, 4 Dec 2022 02:16:49 +0100 Subject: [PATCH] solution: day7 --- src/bin/07.rs | 187 +++++++++++++++++++++++++++++++++++++++++ src/test_inputs/07.txt | 10 +++ 2 files changed, 197 insertions(+) create mode 100644 src/bin/07.rs create mode 100644 src/test_inputs/07.txt diff --git a/src/bin/07.rs b/src/bin/07.rs new file mode 100644 index 000000000..e5df755 --- /dev/null +++ b/src/bin/07.rs @@ -0,0 +1,187 @@ +use hashbrown::{HashMap, HashSet}; +use itertools::Itertools; +use lazy_static::lazy_static; +use regex::Regex; +use Gate::*; +use Operand::*; + +#[derive(Debug, Eq, PartialEq, Hash)] +enum Operand { + Address(String), + Literal(u16), +} + +impl Operand { + fn get(&self, map: &HashMap) -> u16 { + match self { + Address(x) => *map.get(x).unwrap(), + Literal(x) => *x, + } + } +} + +impl From<&str> for Operand { + fn from(value: &str) -> Self { + match value.parse() { + Ok(x) => Literal(x), + Err(_) => Address(value.to_string()), + } + } +} + +#[derive(Debug, Eq, PartialEq, Hash)] +enum Gate { + Write(Operand, Operand), + And(Operand, Operand, Operand), + Or(Operand, Operand, Operand), + LShift(Operand, u8, Operand), + RShift(Operand, u8, Operand), + Not(Operand, Operand), +} + +impl Gate { + fn can_evaluate(&self, map: &HashMap) -> bool { + match self { + Write(Address(x), _) => map.contains_key(x), + Not(Address(x), _) => map.contains_key(x), + LShift(Address(x), _, _) => map.contains_key(x), + RShift(Address(x), _, _) => map.contains_key(x), + And(x, y, _) | Or(x, y, _) => { + let mut allow = true; + if let Address(xx) = x { + allow &= map.contains_key(xx); + }; + if let Address(yy) = y { + allow &= map.contains_key(yy); + }; + allow + } + _ => true, + } + } +} + +impl From<&str> for Gate { + fn from(value: &str) -> Self { + lazy_static! { + static ref RE_SIGNAL: Regex = Regex::new(r"^(\w*) -> (\w*)$").unwrap(); + static ref RE_NOT: Regex = Regex::new(r"NOT (\w*) -> (\w*)").unwrap(); + static ref RE_GATE: Regex = + Regex::new(r"^(\w*) (AND|OR|LSHIFT|RSHIFT) (\w*) -> (\w*)$").unwrap(); + } + + if let Some(cap) = RE_SIGNAL.captures(value) { + return Write(Operand::from(&cap[1]), Operand::from(&cap[2])); + }; + + if let Some(cap) = RE_NOT.captures(value) { + return Not(Operand::from(&cap[1]), Operand::from(&cap[2])); + }; + + if let Some(cap) = RE_GATE.captures(value) { + match &cap[2] { + "AND" => { + return And( + Operand::from(&cap[1]), + Operand::from(&cap[3]), + Operand::from(&cap[4]), + ) + } + "OR" => { + return Or( + Operand::from(&cap[1]), + Operand::from(&cap[3]), + Operand::from(&cap[4]), + ) + } + "LSHIFT" => { + return LShift( + Operand::from(&cap[1]), + cap[3].parse().unwrap(), + Operand::from(&cap[4]), + ); + } + "RSHIFT" => { + return RShift( + Operand::from(&cap[1]), + cap[3].parse().unwrap(), + Operand::from(&cap[4]), + ); + } + _ => unreachable!(), + } + }; + unreachable!() + } +} + +pub fn part_one(input: &str) -> Option { + let mut map: HashMap = HashMap::new(); + let mut set: HashSet = HashSet::new(); + for line in input.trim().split('\n') { + set.insert(Gate::from(line)); + } + while !set.is_empty() { + for gate in set.drain_filter(|x| x.can_evaluate(&map)).collect_vec() { + match gate { + Write(a, Address(b)) => map.insert(b.to_string(), a.get(&map)), + And(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) & b.get(&map)), + Or(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) | b.get(&map)), + LShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) << b), + RShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) >> b), + Not(a, Address(b)) => map.insert(b.to_string(), !a.get(&map)), + _ => unreachable!(), + }; + } + } + Some(*map.get("a").unwrap() as u32) +} +pub fn part_two(input: &str) -> Option { + let mut map: HashMap = HashMap::new(); + let mut set: HashSet = HashSet::new(); + for line in input.trim().split('\n') { + let g = Gate::from(line); + if let Write(a, Address(b)) = g { + if b == "b" { + map.insert("b".to_string(), 46065); + } else { + set.insert(Write(a, Address(b))); + } + } else { + set.insert(g); + } + } + while !set.is_empty() { + for gate in set.drain_filter(|x| x.can_evaluate(&map)).collect_vec() { + match gate { + Write(a, Address(b)) => map.insert(b.to_string(), a.get(&map)), + And(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) & b.get(&map)), + Or(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) | b.get(&map)), + LShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) << b), + RShift(a, b, Address(c)) => map.insert(c.to_string(), a.get(&map) >> b), + Not(a, Address(b)) => map.insert(b.to_string(), !a.get(&map)), + _ => unreachable!(), + }; + } + } + Some(*map.get("a").unwrap() as u32) +} +fn main() { + let input = &aoc::read_file("inputs", 7); + aoc::solve!(1, part_one, input); + aoc::solve!(2, part_two, input); +} +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn test_part_one() { + let input = aoc::read_file("test_inputs", 7); + assert_eq!(part_one(&input), Some(65533)); + } + #[test] + fn test_part_two() { + let input = aoc::read_file("test_inputs", 7); + assert_eq!(part_two(&input), Some(19470)); + } +} diff --git a/src/test_inputs/07.txt b/src/test_inputs/07.txt new file mode 100644 index 000000000..6acc057 --- /dev/null +++ b/src/test_inputs/07.txt @@ -0,0 +1,10 @@ +123 -> x +456 -> y +x AND y -> d +x OR y -> e +x LSHIFT 2 -> f +y RSHIFT 2 -> g +NOT x -> h +NOT y -> i +2 -> b +NOT b -> a