use std::cmp::Ordering; use std::fmt::{Debug, Formatter}; use itertools::Itertools; use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete; use nom::character::complete::newline; use nom::multi::{many1, separated_list0, separated_list1}; use nom::sequence::delimited; use nom::{IResult, Parser}; pub fn process_part_1(input: &str) -> usize { let packets = parse_input(input).unwrap().1; packets .iter() .tuples::<(_, _)>() .enumerate() .filter_map(|(i, (a, b))| match a.cmp(b) { Ordering::Less => Some(i + 1), Ordering::Equal => panic!("oh no"), _ => None, }) .sum() } pub fn process_part_2(input: &str) -> usize { let packets = parse_input(input).unwrap().1; let div_2 = Packet::List(vec![Packet::List(vec![Packet::Value(2)])]); let div_6 = Packet::List(vec![Packet::List(vec![Packet::Value(6)])]); packets .iter() .chain([&div_2, &div_6]) .sorted() .enumerate() .filter(|&(_i, item)| item == &div_2 || item == &div_6) .map(|(i, _)| i + 1) .product() } #[derive(Clone)] enum Packet { List(Vec), Value(u32), } impl Debug for Packet { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { Packet::List(v) => write!(f, "{v:?}"), Packet::Value(v) => write!(f, "{v}"), } } } impl Eq for Packet {} impl PartialEq for Packet { fn eq(&self, other: &Self) -> bool { match (self, other) { (Packet::Value(a), Packet::Value(b)) => a == b, (Packet::List(a), Packet::List(b)) => a == b, (Packet::Value(a), Packet::List(b)) => &vec![Packet::Value(*a)] == b, (Packet::List(a), Packet::Value(b)) => a == &vec![Packet::Value(*b)], } } } impl PartialOrd for Packet { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl Ord for Packet { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (Packet::Value(a), Packet::Value(b)) => a.cmp(b), (Packet::List(a), Packet::List(b)) => a.cmp(b), (Packet::Value(a), Packet::List(b)) => vec![Packet::Value(*a)].cmp(b), (Packet::List(a), Packet::Value(b)) => a.cmp(&vec![Packet::Value(*b)]), } } } // I originally parsed tuples but for part 2 I needed the full list without tuples. // so lets just parse without. We'll just tuple-ify the iterator afterwards. fn parse_input(input: &str) -> IResult<&str, Vec> { separated_list1(many1(newline), parse_packet)(input) } fn parse_packet(input: &str) -> IResult<&str, Packet> { alt(( complete::u32.map(Packet::Value), delimited(tag("["), separated_list0(tag(","), parse_packet), tag("]")).map(Packet::List), ))(input) } #[cfg(test)] mod tests { use super::*; const INPUT: &str = "[1,1,3,1,1] [1,1,5,1,1] [[1],[2,3,4]] [[1],4] [9] [[8,7,6]] [[4,4],4,4] [[4,4],4,4,4] [7,7,7,7] [7,7,7] [] [3] [[[]]] [[]] [1,[2,[3,[4,[5,6,7]]]],8,9] [1,[2,[3,[4,[5,6,0]]]],8,9]"; #[test] fn day1() { assert_eq!(process_part_1(INPUT), 13); } #[test] fn day2() { assert_eq!(process_part_2(INPUT), 140); } }