use std::ops::{Range, RangeInclusive}; use std::str::FromStr; use std::{collections::HashSet, ops::RangeBounds}; use aoc_2022::*; use color_eyre::eyre::{Report, Result}; use tracing::*; use tracing_subscriber::EnvFilter; struct Assignment(RangeInclusive, RangeInclusive); impl FromStr for Assignment { type Err = Report; fn from_str(s: &str) -> Result { let split_s = s.split(',').collect::>(); let (left, right) = (split_s[0], split_s[1]); let (l_split, r_split) = ( left.split('-').collect::>(), right.split('-').collect::>(), ); let (l_min, l_max) = (l_split[0], l_split[1]); let (r_min, r_max) = (r_split[0], r_split[1]); let (ll, lu) = (l_min.parse::()?, l_max.parse::()?); let (rl, ru) = (r_min.parse::()?, r_max.parse::()?); // debug!("Parsed: {}..={}, {}..={}", ll, lu, rl, ru); Ok(Assignment(ll..=lu, rl..=ru)) } } type Input = Vec; type Output = i32; fn part_1(input: &Input) -> Output { input .iter() .filter(|Assignment(l, r)| { (l.start() <= r.start() && r.end() <= l.end()) || (r.start() <= l.start() && l.end() <= r.end()) }) .count() as i32 } fn part_2(input: &Input) -> Output { input .iter() .filter(|Assignment(l, r)| { l.contains(r.start()) || l.contains(r.end()) || r.contains(l.start()) || r.contains(l.end()) }) .count() as i32 } fn parse_input(pathname: &str) -> Input { file_to_structs(pathname) } fn main() -> Result<()> { tracing_subscriber::fmt() .with_env_filter(EnvFilter::from_default_env()) .with_target(false) .with_file(true) .with_line_number(true) .without_time() .compact() .init(); color_eyre::install()?; let input = parse_input("inputs/day04.txt"); info!("Part 1: {}", part_1(&input)); info!("Part 2: {}", part_2(&input)); Ok(()) } #[cfg(test)] mod day_04 { use super::*; #[test] fn test_part_1() { let sample = parse_input("samples/day04.txt"); assert_eq!(part_1(&sample), 2); } #[test] fn test_part_2() { let sample = parse_input("samples/day04.txt"); assert_eq!(part_2(&sample), 4); } }