diff --git a/2023/Cargo.lock b/2023/Cargo.lock index e8d855f..d5a82b4 100644 --- a/2023/Cargo.lock +++ b/2023/Cargo.lock @@ -102,6 +102,15 @@ dependencies = [ "rstest", ] +[[package]] +name = "day-9" +version = "2023.0.0" +dependencies = [ + "itertools", + "nom", + "rstest", +] + [[package]] name = "derive-getters" version = "0.3.0" diff --git a/2023/day-8/src/part1.rs b/2023/day-8/src/part1.rs index eaedcdf..533fe9b 100644 --- a/2023/day-8/src/part1.rs +++ b/2023/day-8/src/part1.rs @@ -31,7 +31,7 @@ impl Branches { } } -/// day 4 part 1 of aoc 2023 +/// day 8 part 1 of aoc 2023 /// /// # Arguments /// - input the input for today's puzzle diff --git a/2023/day-8/src/part2.rs b/2023/day-8/src/part2.rs index 6df2a30..48ea029 100644 --- a/2023/day-8/src/part2.rs +++ b/2023/day-8/src/part2.rs @@ -30,7 +30,7 @@ impl Branches { } } -/// day 4 part 1 of aoc 2023 +/// day 8 part 2 of aoc 2023 /// /// # Arguments /// - input the input for today's puzzle diff --git a/2023/day-9/Cargo.toml b/2023/day-9/Cargo.toml index cb3c178..bf88608 100644 --- a/2023/day-9/Cargo.toml +++ b/2023/day-9/Cargo.toml @@ -10,3 +10,4 @@ repository.workspace = true [dependencies] nom.workspace = true itertools.workspace = true +rstest = {workspace = true} diff --git a/2023/day-9/src/part1.rs b/2023/day-9/src/part1.rs index a7db908..9cb03ad 100644 --- a/2023/day-9/src/part1.rs +++ b/2023/day-9/src/part1.rs @@ -1,20 +1,62 @@ #![warn(clippy::all, clippy::pedantic)] +use nom::{character::complete, multi::separated_list1, IResult}; +use std::{iter::successors, ops::Not}; + +/// day 9 part 1 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than +/// usize #[must_use] -pub fn part1 (_input: &str) -> String { - "Not Finished".to_string() +pub fn part1(input: &str) -> String { + let (_, report) = parse_input(input).expect("should have valid input for aoc"); + report.iter().map(|x| get_next(x)).sum::().to_string() +} + +fn get_next(array: &[i64]) -> i64 { + let array = Vec::from(array); + successors(Some(array), |a| { + a.iter() + .all(|x| x == &0) + .not() + .then_some(a.windows(2).map(|a| a[1] - a[0]).collect::>()) + }) + .map(|x| *x.last().unwrap()) + .sum() +} + +fn parse_input(input: &str) -> IResult<&str, Vec>> { + separated_list1( + complete::line_ending, + separated_list1(complete::space1, complete::i64), + )(input) } #[cfg(test)] mod test { use super::*; + use rstest::rstest; - const INPUT: &str = ""; + #[rstest] + #[case(vec![0,3,6,9,12,15], 18)] + #[case(vec![1,3,6,10,15,21], 28)] + #[case(vec![10,13,16,21,30,45], 68)] + fn part1_next(#[case] array: Vec, #[case] expected: i64) { + assert_eq!(get_next(&array), expected); + } + + const INPUT: &str = "0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45"; #[test] fn part1_works() { let result = part1(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "114".to_string()); } } diff --git a/2023/day-9/src/part2.rs b/2023/day-9/src/part2.rs index 8f15571..bcb4c33 100644 --- a/2023/day-9/src/part2.rs +++ b/2023/day-9/src/part2.rs @@ -1,20 +1,64 @@ #![warn(clippy::all, clippy::pedantic)] +use nom::{character::complete, multi::separated_list1, IResult}; +use std::{iter::successors, ops::Not}; + +/// day 9 part 2 of aoc 2023 +/// +/// # Arguments +/// - input the input for today's puzzle +/// +/// # Panics +/// panics whne it cannot parse the input OR when ever the number of game numbers is greater than +/// usize #[must_use] -pub fn part2 (_input: &str) -> String { - "Not Finished".to_string() +pub fn part2(input: &str) -> String { + let (_, report) = parse_input(input).expect("should have valid input for aoc"); + report.iter().map(|x| get_next(x)).sum::().to_string() +} + +fn get_next(array: &[i64]) -> i64 { + let array = Vec::from(array); + let mut a = successors(Some(array.clone()), |a| { + a.iter() + .all(|x| x == &0) + .not() + .then_some(a.windows(2).map(|a| a[1] - a[0]).collect::>()) + }) + .map(|x| *x.first().unwrap()) + .collect::>(); + a.reverse(); + a.iter().fold(0, |acc, x| x - acc) +} + +fn parse_input(input: &str) -> IResult<&str, Vec>> { + separated_list1( + complete::line_ending, + separated_list1(complete::space1, complete::i64), + )(input) } #[cfg(test)] mod test { use super::*; + use rstest::rstest; - const INPUT: &str = ""; + #[rstest] + #[case(vec![0,3,6,9,12,15], -3)] + #[case(vec![1,3,6,10,15,21], 0)] + #[case(vec![10,13,16,21,30,45], 5)] + fn part2_next(#[case] array: Vec, #[case] expected: i64) { + assert_eq!(get_next(&array), expected); + } + + const INPUT: &str = "0 3 6 9 12 15 +1 3 6 10 15 21 +10 13 16 21 30 45"; #[test] fn part2_works() { let result = part2(INPUT); - assert_eq!(result, "Not Finished".to_string()); + assert_eq!(result, "2".to_string()); } }