diff --git a/.gitignore b/.gitignore index 565e18e..ea1650f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ /target /inputs +/.vscode diff --git a/src/main.rs b/src/main.rs index bfafd1b..395351b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,12 +2,14 @@ use std::env; mod star_one; mod star_two; +mod star_three; fn main() { let args: Vec = env::args().collect(); match args[1].as_str() { "1" => star_one::run(), "2" => star_two::run(), + "3" => star_three::run(), _ => unreachable!(), } } diff --git a/src/star_three.rs b/src/star_three.rs new file mode 100644 index 0000000..1e0b4c2 --- /dev/null +++ b/src/star_three.rs @@ -0,0 +1,128 @@ +use std::fs; + +pub fn run() { + let file = fs::read_to_string("./inputs/star_three.txt").unwrap(); + let input = file.lines(); + let reports = parse_input(input); + let result = count_safe_reports(reports); + println!("Result: {}", result); +} + +fn parse_input<'a, I>(str_lines: I) -> Vec> +where + I: IntoIterator +{ + let mut reports = vec![]; + + for str_line in str_lines { + let parsed_elems: Vec = str_line.split_whitespace().map(|n| n.to_string().parse::().unwrap()).collect(); + reports.push(parsed_elems) + } + + reports +} + +fn count_safe_reports(reports: Vec>) -> usize { + reports.iter().filter(|&report| is_level_change_safe(report)).count() +} + +#[derive(PartialEq, Copy, Clone)] +enum Balancing { + Increasing, + Decreasing, +} + +fn is_level_change_safe(report: &Vec) -> bool { + if report.len() < 2 { + return false; + } + let mut i = 1; + let mut last_balancing = None; + while i < report.len() { + let prev_level = report[i - 1]; + let current_level = report[i]; + + if prev_level == current_level { + return false; + } + + let difference = match current_level > prev_level { + true => current_level - prev_level, + false => prev_level - current_level, + }; + + if difference > 3 { + return false; + } + + if let Some(current_balancing) = last_balancing { + let new_balancing = match current_level > prev_level { + true => Balancing::Increasing, + false => Balancing::Decreasing, + }; + if new_balancing != current_balancing { + return false; + } + } else { + last_balancing = match current_level > prev_level { + true => Some(Balancing::Increasing), + false => Some(Balancing::Decreasing), + } + } + + i += 1; + } + + true +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_parse_input() { + let input = vec![ + "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", + ]; + let result = parse_input(input); + let expected_result = vec![ + vec![7, 6, 4, 2, 1], + vec![1, 2, 7, 8, 9], + vec![9, 7, 6, 2, 1], + vec![1, 3, 2, 4, 5], + vec![8, 6, 4, 4, 1], + vec![1, 3, 6, 7, 9], + ]; + assert_eq!(result, expected_result); + } + + #[test] + fn test_count_safe_reports() { + let reports = vec![ + vec![7, 6, 4, 2, 1], + vec![1, 2, 7, 8, 9], + vec![9, 7, 6, 2, 1], + vec![1, 3, 2, 4, 5], + vec![8, 6, 4, 4, 1], + vec![1, 3, 6, 7, 9], + ]; + assert_eq!(count_safe_reports(reports), 2); + } + + #[test] + fn test_is_level_change_safe() { + assert!(is_level_change_safe(&vec![7, 6, 4, 2, 1])); + assert!(!is_level_change_safe(&vec![1, 2, 7, 8, 9])); + assert!(!is_level_change_safe(&vec![9, 7, 6, 2, 1])); + assert!(!is_level_change_safe(&vec![1, 3, 2, 4, 5])); + assert!(!is_level_change_safe(&vec![8, 6, 4, 4, 1])); + assert!(is_level_change_safe(&vec![1, 3, 6, 7, 9])); + } +}