solution: day7
This commit is contained in:
		
							
								
								
									
										187
									
								
								src/bin/07.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										187
									
								
								src/bin/07.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -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<String, u16>) -> 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<String, u16>) -> 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<u32> { | ||||||
|  |     let mut map: HashMap<String, u16> = HashMap::new(); | ||||||
|  |     let mut set: HashSet<Gate> = 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<u32> { | ||||||
|  |     let mut map: HashMap<String, u16> = HashMap::new(); | ||||||
|  |     let mut set: HashSet<Gate> = 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)); | ||||||
|  |     } | ||||||
|  | } | ||||||
							
								
								
									
										10
									
								
								src/test_inputs/07.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/test_inputs/07.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -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 | ||||||
		Reference in New Issue
	
	Block a user