diff --git a/2022/src/bin/day13.rs b/2022/src/bin/day13.rs new file mode 100644 index 0000000..f3087a5 --- /dev/null +++ b/2022/src/bin/day13.rs @@ -0,0 +1,222 @@ +use std::{cmp::Ordering, fmt::Display, iter::Peekable, str::Chars}; + +const INPUT: &str = include_str!("../../input/day13.txt"); + +fn main() { + let packets = parse(INPUT); + + println!("{:?}", part1(&packets)); + println!("{:?}", part2(&packets)); +} + +fn part1(packets: &[Packet]) -> usize { + packets + .iter() + .enumerate() + .filter_map(|(i, packet)| { + if packet.left < packet.right { + Some(i + 1) + } else { + None + } + }) + .sum() +} + +fn part2(packets: &[Packet]) -> usize { + let p2 = Data::parse("[[2]]"); + let p6 = Data::parse("[[6]]"); + + let mut packets: Vec = packets + .iter() + .flat_map(|p| vec![p.left.clone(), p.right.clone()]) + .collect(); + + packets.sort(); + + let mut i2 = 1; // 1-indexing + let mut i6 = 2; // 1-indexing + p2 placeholder + + for p in packets { + if p < p2 { + i2 += 1; + } + if p < p6 { + i6 += 1; + } + } + + i2 * i6 +} + +fn parse(input: &str) -> Vec { + aoc::blocks(input) + .map(|block| { + let lines: Vec<&str> = aoc::lines(block).collect(); + let left = Data::parse(lines[0]); + let right = Data::parse(lines[1]); + + Packet { left, right } + }) + .collect() +} + +#[derive(Debug, Clone)] +struct Packet { + left: Data, + right: Data, +} + +#[derive(Debug, Clone)] +enum Data { + Value(u32), + List(Vec), +} + +impl Data { + fn parse(line: &str) -> Self { + let mut chars = line.chars().peekable(); + let list = Data::parse_list(&mut chars); + assert!(chars.peek().is_none()); + list + } + + fn parse_num(chars: &mut Peekable) -> Self { + let mut digits = String::new(); + while let Some(c) = chars.peek() { + if c.is_ascii_digit() { + digits += &chars.next().unwrap().to_string(); + } else { + break; + } + } + + Data::Value(digits.parse().unwrap()) + } + + fn parse_list(chars: &mut Peekable) -> Self { + assert!(chars.next() == Some('[')); + + let mut list = vec![]; + while let Some(c) = chars.peek() { + let val = match c { + ']' => { + chars.next(); + break; + } + '[' => Self::parse_list(chars), + ',' => { + chars.next(); + continue; + } + _ => Self::parse_num(chars), + }; + list.push(val); + } + + Data::List(list) + } +} + +impl Data { + fn lift(&self) -> Self { + match self { + Data::List(_) => self.clone(), + Data::Value(_) => Data::List(vec![self.clone()]), + } + } + + fn zip_cmp(l: &[Data], r: &[Data]) -> Ordering { + let mut ll = l.iter(); + let mut rr = r.iter(); + + loop { + match (ll.next(), rr.next()) { + (None, None) => return Ordering::Equal, + (None, Some(_)) => return Ordering::Less, + (Some(_), None) => return Ordering::Greater, + (Some(lll), Some(rrr)) => { + let d = lll.cmp(rrr); + if d != Ordering::Equal { + return d; + } + } + } + } + } +} + +impl PartialEq for Data { + fn eq(&self, other: &Self) -> bool { + self.cmp(other) == Ordering::Equal + } +} + +impl Eq for Data {} + +impl PartialOrd for Data { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Data { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (Data::Value(l), Data::Value(r)) => l.cmp(r), + (Data::List(l), Data::List(r)) => Data::zip_cmp(l, r), + + (Data::Value(_), Data::List(_)) => self.lift().cmp(other), + (Data::List(_), Data::Value(_)) => self.cmp(&other.lift()), + } + } +} + +impl Display for Data { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Data::Value(v) => write!(f, "{}", v), + Data::List(l) => { + let items = l + .iter() + .map(|v| v.to_string()) + .collect::>() + .join(","); + + write!(f, "[{}]", items) + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test1() { + // let line = "[[[[2],9,1,[2,2,4,8]],[1,8,8],9,[7,2,[7,0,1],0,[10,9,10,3]]]]"; + // let line = "[[[2],9,1,[2,2,4,8]],[1,8,8],9,[7,2,[7,0,1],0,[10,9,10,3]]]"; + // let line = "[[2],9,1,[2,2,4,8]],[1,8,8],9,[7,2,[7,0,1],0,[10,9,10,3]]"; + // let line = "[[2],9,1,[2,2,4,8]]"; + let line = "[[2],9]"; + let data = Data::parse(line); + + assert_eq!(data.to_string(), line); + + use Data::{List, Value}; + + assert_eq!(data, List(vec![List(vec![Value(2)]), Value(9)])) + } + + #[test] + fn test_parser() { + let out = parse(INPUT) + .iter() + .map(|p| format!("{}\n{}", p.left, p.right)) + .collect::>() + .join("\n\n") + + "\n"; + assert_eq!(out, INPUT); + } +}