use std::cmp::Ordering; use crate::common::{parse_input, Level}; fn task1() { let raw_input = include_str!("./input.txt"); let safe_levels = parse_input(raw_input) .into_iter() .map(LevelState::from) .filter(LevelState::is_all_safe) .count(); println!("The solution to task 1 is {safe_levels}") } #[derive(PartialOrd, PartialEq, Debug, Copy, Clone)] pub(crate) enum State { Increase(usize), Decrease(usize), Stable, } impl State { pub(crate) fn from_values(first: usize, second: usize) -> Self { match second.cmp(&first) { Ordering::Less => Self::Decrease(first.abs_diff(second)), Ordering::Greater => Self::Increase(first.abs_diff(second)), Ordering::Equal => Self::Stable, } } pub(crate) fn is_safely_increasing(&self) -> bool { matches!(self, State::Increase(1..=3)) } pub(crate) fn is_safely_decreasing(&self) -> bool { matches!(self, State::Decrease(1..=3)) } } pub(crate) struct LevelState(pub(crate) Vec); impl From for LevelState { fn from(level: Level) -> Self { let mut previous = None; let mut level_state = Vec::with_capacity(level.0.len()); for current in level.0 { if let Some(previous) = previous { level_state.push(State::from_values(previous, current)) } else { level_state.push(State::Stable); } previous = Some(current); } Self(level_state) } } impl LevelState { pub(crate) fn is_all_safe(&self) -> bool { self.is_all_safely_increasing() || self.is_all_safely_decreasing() } fn is_all_safely_increasing(&self) -> bool { self.is_all_changing(State::is_safely_increasing) } fn is_all_safely_decreasing(&self) -> bool { self.is_all_changing(State::is_safely_decreasing) } fn is_all_changing(&self, verify: fn(&State) -> bool) -> bool { let states = &self.0; match &states[..] { [State::Stable, tail @ ..] if tail.iter().all(verify) => true, [State::Stable] | [] => true, _ => false, } } }