use std::{ cell::RefCell, collections::{HashMap, HashSet}, io::{self, Read}, }; #[allow(unused_macros)] macro_rules! todo { ($($tt:tt)*) => { unimplemented!($($tt)*) }; } type Result = std::result::Result>; fn get_input_string() -> io::Result { let mut buffer = String::new(); io::stdin().read_to_string(&mut buffer)?; Ok(buffer) } fn main() -> Result<()> { let orbit_list: Vec = get_input_string()? .trim() .lines() .map(|line| { let mut split = line.split(")"); Orbit { parent: split.next().unwrap().to_string(), child: split.next().unwrap().to_string(), } }).collect(); let mut orbit_graph = OrbitGraph::default(); for orbit in orbit_list.iter() { orbit_graph.add_orbit(orbit.clone()); } part1(&orbit_list, &orbit_graph); part2(&orbit_list, &orbit_graph); Ok(()) } fn part1(orbit_list: &Vec, orbit_graph: &OrbitGraph) { let bodies: HashSet = orbit_list.iter() .map(|o| o.child.clone()) .collect(); let total: usize = bodies.iter() .map(|body| orbit_graph.indirect_count(body)) .sum(); println!("Part 1: {}", total); } fn part2(orbit_list: &Vec, orbit_graph: &OrbitGraph) { let common = orbit_graph.common_parent("YOU", "SAN") .expect("no common parent between YOU and SAN"); let common_dist = orbit_graph.indirect_count(common); let hops1 = orbit_graph.indirect_count("YOU") - common_dist - 1; let hops2 = orbit_graph.indirect_count("SAN") - common_dist - 1; println!("Part 2: {}", hops1 + hops2); } #[derive(Debug, Clone)] struct Orbit { parent: String, child: String, } #[derive(Debug, Default)] struct OrbitGraph { graph: HashMap>, orbits: HashMap, count_cache: RefCell>, } impl OrbitGraph { fn add_orbit(&mut self, Orbit { parent, child }: Orbit) { let child_list = self.graph.entry(parent.clone()).or_insert(Vec::new()); child_list.push(child.clone()); assert!(self.orbits.insert(child, parent).is_none()); } fn indirect_count(&self, body: &str) -> usize { { let cache = self.count_cache.borrow(); if let Some(count) = cache.get(body) { return *count; } } if let Some(parent) = self.orbits.get(body) { let count = 1 + self.indirect_count(parent); { let mut cache = self.count_cache.borrow_mut(); cache.insert(body.to_string(), count); } count } else { 0 } } fn common_parent(&self, first: &str, second: &str) -> Option<&str> { let first_parent = self.orbits.get(first); let second_parent = self.orbits.get(second); // One of the parents is the root, which is the common parent for everyone if first_parent.is_none() || second_parent.is_none() { return None; } let first_parent = first_parent.unwrap(); let second_parent= second_parent.unwrap(); // Common parent found if first_parent == second_parent { return Some(first_parent); } let depth1 = self.indirect_count(first); let depth2 = self.indirect_count(second); if depth1 < depth2 { self.common_parent(first, second_parent) } else if depth2 < depth1 { self.common_parent(first_parent, second) } else { self.common_parent(first_parent, second_parent) } } }