use anyhow::Result; use std::cmp::Ordering; use std::fs; #[derive(Debug, Clone, Eq, PartialEq)] enum Packet { Num(i32), List(Vec), } fn parse(s: &str) -> Packet { if &s[0..1] == "[" { let mut stack: i32 = 0; Packet::List( s[1..s.len() - 1] .split(|c| { if c == '[' { stack += 1 } else if c == ']' { stack -= 1 } c == ',' && stack == 0 }) .filter_map(|s| (!s.is_empty()).then(|| parse(s))) .collect(), ) } else { Packet::Num(s.parse().unwrap()) } } impl Ord for Packet { fn cmp(&self, other: &Self) -> Ordering { match (self, other) { (Packet::Num(a), Packet::Num(b)) => a.cmp(b), (Packet::Num(_), _) => Packet::List(vec![self.clone()]).cmp(other), (_, Packet::Num(_)) => self.cmp(&Packet::List(vec![other.clone()])), (Packet::List(a), Packet::List(b)) => a.cmp(b), } } } impl PartialOrd for Packet { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } pub fn run() -> Result<()> { #[cfg(not(feature = "test_input"))] let file_contents = fs::read_to_string("day13.txt")?; #[cfg(feature = "test_input")] let file_contents = fs::read_to_string("tests/day13.txt")?; let mut packets: Vec = file_contents .trim() .lines() .filter_map(|s| (!s.is_empty()).then(|| parse(s))) .collect(); let mut part1 = 0; for (packet_index, packet_pair) in packets.chunks(2).enumerate() { let o = packet_pair[0].cmp(&packet_pair[1]); assert!(o != Ordering::Equal); if o == Ordering::Less { part1 += packet_index + 1; } } println!("part one: {}", part1); let div1 = parse("[[2]]"); let div2 = parse("[[6]]"); packets.push(div1.clone()); packets.push(div2.clone()); packets.sort(); let i1 = packets.binary_search(&div1).unwrap() + 1; let i2 = packets.binary_search(&div2).unwrap() + 1; println!("part two: {} * {} = {}", i1, i2, i1 * i2); Ok(()) }