use core::slice; use std::cmp::Ordering; use anyhow::Result; use nom::branch::alt; use nom::bytes::complete::tag; use nom::character::complete::multispace1; use nom::character::complete::newline; use nom::combinator::iterator; use nom::combinator::map; use nom::multi::separated_list0; use nom::sequence::delimited; use nom::sequence::pair; use nom::sequence::terminated; use nom::IResult; #[derive(Debug, PartialEq, Eq, Clone)] enum Signal { Number(u32), List(Vec), } impl Ord for Signal { fn cmp(&self, other: &Self) -> Ordering { fn list_cmp(a: &[Signal], b: &[Signal]) -> Ordering { a.cmp(b) } match (self, other) { (Signal::Number(first), Signal::Number(second)) => first.cmp(second), (first @ Signal::Number(_), Signal::List(second)) => { list_cmp(slice::from_ref(first), second) } (Signal::List(first), second @ Signal::Number(_)) => { list_cmp(first, slice::from_ref(second)) } (Signal::List(first), Signal::List(second)) => list_cmp(first, second), } } } impl PartialOrd for Signal { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } impl From for Signal { fn from(val: u32) -> Self { Signal::Number(val) } } impl From> for Signal where Signal: From, { fn from(vec: Vec) -> Self { Signal::List(vec.into_iter().map(Signal::from).collect()) } } fn parse_signal(input: &[u8]) -> IResult<&[u8], Signal> { alt(( map(nom::character::complete::u32, Signal::Number), map( delimited(tag("["), separated_list0(tag(","), parse_signal), tag("]")), Signal::List, ), ))(input) } fn parse_signal_pair(input: &[u8]) -> IResult<&[u8], (Signal, Signal)> { pair( terminated(parse_signal, newline), terminated(parse_signal, multispace1), )(input) } pub fn part1(input: &[u8]) -> Result { let mut iterator = iterator(input, parse_signal_pair); let result: usize = (&mut iterator) .enumerate() .filter_map(|(i, (first, second))| (first < second).then_some(i + 1)) .sum(); Ok(result.to_string()) } pub fn part2(input: &[u8]) -> Result { let marker1 = Signal::Number(2); let marker2 = Signal::Number(6); let mut iterator = iterator(input, terminated(parse_signal, multispace1)); let mut pos1 = 1; let mut pos2 = 2; for signal in &mut iterator { if signal < marker1 { pos1 += 1; pos2 += 1; } else if signal < marker2 { pos2 += 1; } } Ok((pos1 * pos2).to_string()) } #[cfg(test)] mod tests { use super::*; const SAMPLE: &[u8] = include_bytes!("samples/13.txt"); #[test] fn test_parse_signal() { assert_eq!( parse_signal(b"[1,1,3,1,1]").unwrap(), (&[][..], Signal::from(vec![1, 1, 3, 1, 1])) ); } #[test] fn sample_part1() { assert_eq!(part1(SAMPLE).unwrap(), "13"); } #[test] fn sample_part2() { assert_eq!(part2(SAMPLE).unwrap(), "140"); } }