Day 2
This commit is contained in:
parent
5bb3c29741
commit
ab3c6d20bc
139
2024/rust/src/day2.rs
Normal file
139
2024/rust/src/day2.rs
Normal file
|
@ -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<i64>,
|
||||
}
|
||||
|
||||
impl FromStr for Report {
|
||||
type Err = ParseIntError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let levels = s
|
||||
.split(" ")
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.parse())
|
||||
.collect::<Result<Vec<i64>, 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<LevelSafeKind> = 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<i64> = 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<LevelSafeKind>) -> 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<Report>,
|
||||
}
|
||||
|
||||
impl FromStr for Data {
|
||||
type Err = ParseIntError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let reports = s
|
||||
.lines()
|
||||
.map(|s| s.parse())
|
||||
.collect::<Result<Vec<Report>, 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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue