diff --git a/2024/rust/src/day2.rs b/2024/rust/src/day2.rs new file mode 100644 index 0000000..967b25d --- /dev/null +++ b/2024/rust/src/day2.rs @@ -0,0 +1,139 @@ +mod prelude; +pub use crate::prelude::*; + +fn main() { + let data: Data = day_input(2).parse().unwrap(); + show_answers(data.num_safe(), data.num_safe_with_dampener()); +} + +#[derive(Default, Debug)] +struct Report { + levels: Vec, +} + +impl FromStr for Report { + type Err = ParseIntError; + fn from_str(s: &str) -> Result { + let levels = s + .split(" ") + .filter(|s| !s.is_empty()) + .map(|s| s.parse()) + .collect::, ParseIntError>>()?; + Ok(Self { levels }) + } +} + +#[derive(PartialEq, Eq)] +enum LevelSafeKind { + Increasing, + Decreasing, +} + +impl Report { + fn is_safe(&self) -> bool { + Self::is_level_set_safe(&self.levels) + } + + fn is_level_set_safe(levels: &[i64]) -> bool { + let mut iter = levels.windows(2); + let mut level_safe_kind: Option = None; + + while let Some(&[a, b]) = iter.next() { + if !Self::safe_level_pair(a, b, &level_safe_kind) { + return false; + } + if level_safe_kind == None { + if a > b { + level_safe_kind = Some(LevelSafeKind::Increasing) + } else { + level_safe_kind = Some(LevelSafeKind::Decreasing) + } + } + } + return true; + } + + fn is_safe_with_any_single_level_removed(&self) -> bool { + // TODO: this allocates for each level combination and so is suboptimal + // best solution I think is to update is_level_set_safe to take index pairs or something and slide those in such a way that we skip one of the indices + for i in 0..self.levels.len() { + let levels: Vec = self + .levels + .clone() + .iter() + .enumerate() + .filter(|&(index, _val)| i != index) + .map(|(_index, val)| *val) + .collect(); + if Self::is_level_set_safe(&levels) { + return true; + } + } + return false; + } + + fn safe_level_pair(a: i64, b: i64, kind: &Option) -> bool { + if a == b { + return false; + } + if (a - b).abs() > 3 { + return false; + } + if a - b < 0 && *kind == Some(LevelSafeKind::Increasing) { + return false; + } + if a - b > 0 && *kind == Some(LevelSafeKind::Decreasing) { + return false; + } + return true; + } +} + +#[derive(Default)] +struct Data { + reports: Vec, +} + +impl FromStr for Data { + type Err = ParseIntError; + fn from_str(s: &str) -> Result { + let reports = s + .lines() + .map(|s| s.parse()) + .collect::, ParseIntError>>()?; + Ok(Self { reports }) + } +} + +impl Data { + fn num_safe(&self) -> usize { + self.reports.iter().filter(|r| r.is_safe()).count() + } + + fn num_safe_with_dampener(&self) -> usize { + self.reports + .iter() + .filter(|r| r.is_safe_with_any_single_level_removed()) + .count() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test() { + let mut report: Data = "7 6 4 2 1 +1 2 7 8 9 +9 7 6 2 1 +1 3 2 4 5 +8 6 4 4 1 +1 3 6 7 9" + .parse() + .unwrap(); + assert_eq!(report.num_safe(), 2); + assert_eq!(report.num_safe_with_dampener(), 4); + // assert_eq!(report.(), 31); + } +}