/ Rust / 2024 / 02.rs
02.rs
  1  #![feature(test)]
  2  
  3  use itertools::Itertools;
  4  
  5  type Input = Vec<Vec<i32>>;
  6  
  7  fn setup(input: &str) -> Input {
  8      input
  9          .lines()
 10          .map(|line| {
 11              line.split_whitespace()
 12                  .map(|x| x.parse().unwrap())
 13                  .collect()
 14          })
 15          .collect()
 16  }
 17  
 18  #[derive(Debug, Clone, Copy, Default)]
 19  struct DiffCounts {
 20      positive: usize,
 21      negative: usize,
 22      invalid: usize,
 23  }
 24  
 25  impl DiffCounts {
 26      fn from_nums(nums: &[i32]) -> Self {
 27          nums.iter()
 28              .tuple_windows()
 29              .fold(Self::default(), |c, (a, b)| c.add(b - a))
 30      }
 31  
 32      fn classify(diff: i32) -> Self {
 33          Self {
 34              positive: (1..=3).contains(&diff) as _,
 35              negative: (-3..=-1).contains(&diff) as _,
 36              invalid: (diff == 0 || diff.abs() > 3) as _,
 37          }
 38      }
 39  
 40      fn add(self, diff: i32) -> Self {
 41          let other = Self::classify(diff);
 42          Self {
 43              positive: self.positive + other.positive,
 44              negative: self.negative + other.negative,
 45              invalid: self.invalid + other.invalid,
 46          }
 47      }
 48  
 49      fn sub(self, diff: i32) -> Self {
 50          let other = Self::classify(diff);
 51          Self {
 52              positive: self.positive - other.positive,
 53              negative: self.negative - other.negative,
 54              invalid: self.invalid - other.invalid,
 55          }
 56      }
 57  
 58      fn check(self) -> bool {
 59          self.invalid == 0 && (self.positive == 0 || self.negative == 0)
 60      }
 61  }
 62  
 63  fn part1(input: &Input) -> usize {
 64      input
 65          .iter()
 66          .filter(|nums| DiffCounts::from_nums(nums).check())
 67          .count()
 68  }
 69  
 70  fn check_with_tolerance(nums: &[i32]) -> bool {
 71      let counts = nums
 72          .iter()
 73          .tuple_windows()
 74          .fold(DiffCounts::default(), |c, (a, b)| c.add(b - a));
 75  
 76      if counts.check()
 77          || counts.sub(nums[1] - nums[0]).check()
 78          || counts
 79              .sub(nums[nums.len() - 1] - nums[nums.len() - 2])
 80              .check()
 81      {
 82          return true;
 83      }
 84  
 85      nums.iter()
 86          .tuple_windows()
 87          .any(|(a, b, c)| counts.sub(b - a).sub(c - b).add(c - a).check())
 88  }
 89  
 90  fn part2(input: &Input) -> usize {
 91      input
 92          .iter()
 93          .filter(|nums| check_with_tolerance(nums))
 94          .count()
 95  }
 96  
 97  aoc::main!(2024, 2, ex: 1);
 98  
 99  #[cfg(test)]
100  mod tests {
101      use super::*;
102  
103      #[test]
104      fn wolflu() {
105          assert!(check_with_tolerance(&[18, 21, 23, 22, 23, 26, 28]));
106      }
107  }