use std::cmp::Ordering; use crate::day_solver::DaySolver; use super::util; pub struct Day13 { packet_pairs: Vec<(Packet, Packet)> } impl Day13 { pub fn create() -> Self { // let lines = util::read_file("input/day13_example.txt"); let lines = util::read_file("input/day13.txt"); let mut line_iter = lines.iter(); let mut packet_pairs = Vec::new(); while let Some(line) = line_iter.next() { if line.is_empty() { continue } let left = Packet::parse(line); let right = Packet::parse(line_iter.next().unwrap()); // println!("left: {:?}, right: {:?}", left, right); packet_pairs.push((left, right)); } // println!("{:?}", packet_pairs); // Put the input into the day struct return Day13 { packet_pairs } } fn compare(left: &Packet, right: &Packet) -> Ordering { if let Packet::Num(n_left) = left { if let Packet::Num(n_right) = right { if n_left != n_right { return if n_left < n_right { Ordering::Less } else { Ordering::Greater }; } } else { // The right value is a vec-packet, so we need to act like the left value is as well: return Self::compare(&Packet::Vec(vec![Packet::Num(n_left.to_owned())]), right); } } else if let Packet::Vec(vec_left) = left { // Left is a vec-packet if let Packet::Vec(vec_right) = right { for i in 0..vec_left.len().min(vec_right.len()) { let comp = Self::compare(&vec_left[i], &vec_right[i]); if comp != Ordering::Equal { return comp } } if vec_left.len() != vec_right.len() { return if vec_left.len() < vec_right.len() { Ordering::Less } else { Ordering::Greater }; } } else if let Packet::Num(n_right) = right { // The left value is a vec-packet, so we need to act like the right value is as well: return Self::compare(left, &Packet::Vec(vec![Packet::Num(n_right.to_owned())])); } } return Ordering::Equal; } } impl DaySolver for Day13 { fn solve_part1(&mut self) -> String { self.packet_pairs.iter().enumerate() .filter_map(|(i, p)| { let comp = Day13::compare(&p.0, &p.1); // println!("{:?} vs {:?} is {:?}", p.0, p.1, comp); if comp == Ordering::Less { Some(i + 1) } else { None } }) .sum::().to_string() } fn solve_part2(&mut self) -> String { let mut full_vec: Vec = self.packet_pairs.iter() .flat_map(|p| vec![p.0.to_owned(), p.1.to_owned()].into_iter()) .collect(); let div_packet1 = Packet::parse("[[2]]"); full_vec.push(div_packet1.to_owned()); let div_packet2 = Packet::parse("[[6]]"); full_vec.push(div_packet2.to_owned()); full_vec.sort_by(|l, r| Day13::compare(l, r)); full_vec.iter().enumerate() .filter_map(|(i, p)| if p == &div_packet1 || p == &div_packet2 { Some(i + 1) } else { None }) .reduce(|a, b| a * b) .unwrap() .to_string() } } #[derive(Debug, Clone, PartialEq)] enum Packet { Num(u8), Vec(Vec) } impl Packet { fn parse(str: &str) -> Packet { let (p, _) = Self::parse_internal(str); p } fn parse_internal(str: &str) -> (Packet, usize) { if str.chars().next() == Some('[') { if str.starts_with("[]") { // We're dealing with an empty vec: (Packet::Vec(Vec::new()), 2) } else { // We're dealing with a vec let mut idx = 0; let mut vec_content = Vec::new(); loop { idx += 1; let (p, i) = Packet::parse_internal(&str[idx..]); vec_content.push(p); idx += i; if str.chars().nth(idx) != Some(',') { break; } }; assert_eq!(str.chars().nth(idx), Some(']'), "Vec not ending: {} at idx {}", str, idx); (Packet::Vec(vec_content), idx + 1) } } else { // We should be dealing with a number let n_str = str.chars().take_while(|c| c.is_numeric()).collect::(); let n = n_str.parse::().expect(format!("Vec is not a number: {}", str).as_str()); (Packet::Num(n), n_str.len()) } } }