const DATA: &'static str = include_str!("../data/day4.txt"); // const DATA: &'static str = " // 2-4,6-8 // 2-3,4-5 // 5-7,7-9 // 2-8,3-7 // 6-6,4-6 // 2-6,4-8 // "; #[derive(Debug)] enum DataError { NotANumber, NotAPair, NotARange, } struct Range { start: u32, end: u32, } impl Range { fn contains(&self, other: &Range) -> bool { self.start <= other.start && self.end >= other.end } fn overlaps(&self, other: &Range) -> bool { // what if self.start == other.start or self.end == other.end? if self.start <= other.start && self.end >= other.start { return true; } if self.start <= other.end && self.end >= other.end { return true; } false } } impl std::str::FromStr for Range { type Err = DataError; fn from_str(s: &str) -> Result { let mut bounds = s.split('-'); let start = bounds.next() .ok_or(DataError::NotARange)? .parse::() .map_err(|_e| DataError::NotANumber)?; let end = bounds.next() .ok_or(DataError::NotARange)? .parse::() .map_err(|_e| DataError::NotANumber)?; Ok(Range {start, end}) } } fn main() -> Result<(), DataError> { let ranges = DATA.trim() .lines() .map(|line| { let mut pairs = line.split(','); let left: Range = pairs.next() .ok_or(DataError::NotAPair)? .parse()?; let right: Range = pairs.next() .ok_or(DataError::NotAPair)? .parse()?; Ok((left, right)) }) .collect::, DataError>>()?; let total_supersets = ranges.iter() .filter(|(a, b)| a.contains(b) || b.contains(a)) .count(); println!("Part 1: {total_supersets}"); let total_overlaps = ranges.iter() .filter(|(a, b)| a.overlaps(b) || b.contains(a) || a.contains(b)) .count(); println!("Part 2: {total_overlaps}"); Ok(()) }