use std::collections::HashMap;
solution_printer!(6, print_solution, input_generator, INPUT, solve_part_1, solve_part_2);
pub const INPUT: &str = include_str!("../input/2019/day6.txt");
pub fn solve_part_1<'a, T> (input_orbit_pairs: &mut T) -> u32
where T: Iterator<Item = OrbitPair<'a>>
{
let orbits = orbits_from_orbit_pairs(input_orbit_pairs);
get_total_number_of_orbits_direct_and_indirect(&orbits, "COM", 0)
}
fn get_total_number_of_orbits_direct_and_indirect (orbits: &Orbits, body: &str, num_others_orbited_by_body: u32) -> u32
{
match orbits.get(body)
{
None => num_others_orbited_by_body,
Some(orbiters) =>
{
num_others_orbited_by_body + orbiters.iter().map(|&orbiter|
get_total_number_of_orbits_direct_and_indirect(orbits, orbiter, num_others_orbited_by_body + 1)).sum::<u32>()
},
}
}
pub type OrbitPair<'a> = (&'a str, &'a str);
pub type Orbits<'a> = HashMap<&'a str, Vec<&'a str>>;
pub type Orbiting<'a> = HashMap<&'a str, &'a str>;
pub fn orbits_from_orbit_pairs<'a, T> (orbit_pairs: &mut T) -> Orbits<'a>
where T: Iterator<Item = OrbitPair<'a>>
{
let mut orbits = Orbits::new();
for (orbited, orbiter) in orbit_pairs
{
let orbiters = orbits.entry(orbited).or_insert(Default::default());
orbiters.push(orbiter);
}
orbits
}
pub fn input_generator (input: &'static str) -> impl Iterator<Item = OrbitPair>
{
input.lines().map(|orbit_pair_str|
{
let (orbited, remain) = orbit_pair_str.split_at(orbit_pair_str.find(")").unwrap());
let orbiter = &remain[1..];
(orbited, orbiter)
})
}
pub fn solve_part_2<'a, T> (input_orbit_pairs: &mut T) -> usize
where T: Iterator<Item = OrbitPair<'a>>
{
let orbiting = orbiting_from_orbit_pairs(input_orbit_pairs);
let mut you_orbit = *orbiting.get("YOU").unwrap();
let mut santa_orbits = *orbiting.get("SAN").unwrap();
let mut you_orbits_from_com = vec![];
while you_orbit != "COM"
{
you_orbits_from_com.push(you_orbit);
you_orbit = *orbiting.get(you_orbit).unwrap();
}
drop(you_orbit);
let mut santa_orbits_from_com = vec![];
while santa_orbits != "COM"
{
santa_orbits_from_com.push(santa_orbits);
santa_orbits = *orbiting.get(santa_orbits).unwrap();
}
drop(santa_orbits);
let mut distance_between_you_and_santa = 0;
let mut you_orbits_from_com: &[&str] = &*you_orbits_from_com;
if you_orbits_from_com.len() > santa_orbits_from_com.len()
{
let num_orbits_from_com_diff = you_orbits_from_com.len() - santa_orbits_from_com.len();
you_orbits_from_com = &you_orbits_from_com[num_orbits_from_com_diff..];
distance_between_you_and_santa += num_orbits_from_com_diff;
}
let mut santa_orbits_from_com: &[&str] = &*santa_orbits_from_com;
if santa_orbits_from_com.len() > santa_orbits_from_com.len()
{
let num_orbits_from_com_diff = santa_orbits_from_com.len() - you_orbits_from_com.len();
santa_orbits_from_com = &santa_orbits_from_com[num_orbits_from_com_diff..];
distance_between_you_and_santa += num_orbits_from_com_diff;
}
if you_orbits_from_com.len() > 0
{
while you_orbits_from_com[0] != santa_orbits_from_com[0]
{
you_orbits_from_com = &you_orbits_from_com[1..];
santa_orbits_from_com = &santa_orbits_from_com[1..];
distance_between_you_and_santa += 2;
}
}
distance_between_you_and_santa
}
pub fn orbiting_from_orbit_pairs<'a, T> (orbit_pairs: &mut T) -> Orbiting<'a>
where T: Iterator<Item = OrbitPair<'a>>
{
orbit_pairs.map(|(orbited, orbiter)| (orbiter, orbited)).collect()
}