diff --git a/2/Cargo.lock b/2/Cargo.lock new file mode 100644 index 0000000..63c2f60 --- /dev/null +++ b/2/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "day2" +version = "0.1.0" diff --git a/2/Cargo.toml b/2/Cargo.toml new file mode 100644 index 0000000..cd3d088 --- /dev/null +++ b/2/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "day2" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/2/src/main.rs b/2/src/main.rs new file mode 100644 index 0000000..e1688bf --- /dev/null +++ b/2/src/main.rs @@ -0,0 +1,129 @@ +use std::fs::File; +use std::io::{BufRead, BufReader, Lines}; +use std::time::{Duration, Instant}; + +// BOILERPLATE +type InputIter = Lines>; + +fn get_input() -> InputIter { + let f = File::open("input").unwrap(); + let br = BufReader::new(f); + br.lines() +} + +fn duration_format(duration: &Duration) -> String { + match duration.as_secs_f64() { + x if x > 1.0 => format!("{:.3}s", x), + x if x > 0.010 => format!("{:.3}ms", x * 1e3), + x => format!("{:.3}us", x * 1e6), + } +} + +fn main() { + let start = Instant::now(); + let ans1 = problem1(get_input()); + let duration = start.elapsed(); + println!( + "Problem 1 solution: {} [{}]", + ans1, + duration_format(&duration) + ); + + let start = Instant::now(); + let ans2 = problem2(get_input()); + let duration = start.elapsed(); + println!( + "Problem 2 solution: {} [{}]", + ans2, + duration_format(&duration) + ); +} + +struct Reports { + reports: Vec>, +} + +impl From> for Reports { + fn from(lines: Lines) -> Self { + let mut reports = Vec::new(); + for line in lines.map(|i| i.unwrap()) { + reports.push( + line.split_ascii_whitespace() + .map(|record| record.parse::().unwrap()) + .collect(), + ) + } + Reports { reports } + } +} + +impl Reports { + fn is_safe(report: &Vec) -> bool { + (report.iter().zip(report.iter().skip(1)).all(|(a, b)| a > b) + || report.iter().zip(report.iter().skip(1)).all(|(a, b)| a < b)) + && report + .iter() + .zip(report.iter().skip(1)) + .all(|(a, b)| a.abs_diff(*b) >= 1 && a.abs_diff(*b) <= 3) + } + fn count_safe(&self) -> u64 { + self.reports.iter().filter(|report| Self::is_safe(report)).count() as u64 + } + fn is_dumb_dampened_safe(report: &Vec) -> bool { + if Self::is_safe(report) { + return true; + } + for i in 0..report.len() { + let mut new_vec = report.clone(); + new_vec.remove(i); + if Self::is_safe(&new_vec) { + return true; + } + } + false + } + fn dampened_count_safe(&self) -> u64 { + self.reports + .iter() + .filter(|report| Self::is_dumb_dampened_safe(report)) + .count() as u64 + } +} + +// PROBLEM 1 solution + +fn problem1(input: Lines) -> u64 { + let reports = Reports::from(input); + reports.count_safe() +} + +// PROBLEM 2 solution +fn problem2(input: Lines) -> u64 { + let reports = Reports::from(input); + reports.dampened_count_safe() +} + +#[cfg(test)] +mod tests { + use crate::*; + use std::io::Cursor; + + const EXAMPLE: &str = &"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"; + + #[test] + fn problem1_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem1(c.lines()), 2); + } + + #[test] + fn problem2_example() { + let c = Cursor::new(EXAMPLE); + assert_eq!(problem2(c.lines()), 4); + } +}