diff --git a/src/day_06/mod.rs b/src/day_06/mod.rs new file mode 100644 index 0000000..ae631dd --- /dev/null +++ b/src/day_06/mod.rs @@ -0,0 +1,65 @@ +use aoc_runner_derive::{aoc, aoc_generator}; +use yap::IntoTokens; + +mod model; +mod parsing; + +pub use model::{Fish, School, SmartSchool}; +pub use parsing::parse_school_of_fish; + +#[aoc_generator(day6)] +pub fn parse_input(input: &str) -> School { + parse_school_of_fish(&mut input.into_tokens()) +} + +#[aoc(day6, part1)] +pub fn part1(input: &School) -> usize { + let mut school = input.clone(); + for _ in 0..80 { + school.tick() + } + school.size() +} + +#[aoc(day6, part1, smart_school)] +pub fn part1_smart(input: &School) -> usize { + let mut school = SmartSchool::from(input); + for _ in 0..80 { + school.tick() + } + school.size() +} + +#[aoc(day6, part2)] +pub fn part2(input: &School) -> usize { + let mut school = SmartSchool::from(input); + for _ in 0..256 { + school.tick() + } + school.size() +} + +#[cfg(test)] +mod test { + const EXAMPLE_INPUT: &str = "3,4,3,1,2"; + const RESULT_PART_1: usize = 5934; + const RESULT_PART_2: usize = 26_984_457_539; + + #[test] + fn part1_example() { + let result = super::part1(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_1); + } + + #[test] + fn part2_example() { + let result = super::part2(&super::parse_input(EXAMPLE_INPUT)); + assert_eq!(result, RESULT_PART_2); + } + + #[test] + fn parse_example() { + let example = super::parse_input(EXAMPLE_INPUT); + assert!(example.size() == 5); + } +} diff --git a/src/day_06/model.rs b/src/day_06/model.rs new file mode 100644 index 0000000..0478b02 --- /dev/null +++ b/src/day_06/model.rs @@ -0,0 +1,119 @@ +#[derive(Debug, PartialEq, Copy, Clone)] +pub struct Fish { + pub timer: u8, +} + +impl Default for Fish { + fn default() -> Fish { + Fish::new(8) + } +} + +impl Fish { + fn tick(&mut self) -> Option { + match self.timer { + 0 => { + self.timer = 6; + Some(Fish::default()) + } + _ => { + self.timer -= 1; + None + } + } + } + + fn new(timer: u8) -> Fish { + Fish { timer } + } +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct School { + pub fishes: Vec, +} + +impl School { + pub fn tick(&mut self) { + let mut spawned = Vec::new(); + for fish in self.fishes.iter_mut() { + if let Some(fish) = fish.tick() { + spawned.push(fish); + } + } + self.fishes.append(&mut spawned); + } + + pub fn add(&mut self, fish: Fish) { + self.fishes.push(fish); + } + + pub fn size(&self) -> usize { + self.fishes.len() + } +} + +#[derive(Debug, PartialEq, Clone, Default)] +pub struct SmartSchool { + pub fish_counter: [usize; 9], +} + +impl SmartSchool { + pub fn tick(&mut self) { + let new_fish = self.fish_counter[0]; + for i in 1..self.fish_counter.len() { + self.fish_counter[i - 1] = self.fish_counter[i]; + } + self.fish_counter[8] = new_fish; + self.fish_counter[6] += new_fish; + } + + pub fn add(&mut self, fish: Fish) { + self.fish_counter[fish.timer as usize] += 1; + } + + pub fn size(&self) -> usize { + self.fish_counter.iter().sum() + } +} + +impl From<&School> for SmartSchool { + fn from(school: &School) -> Self { + let mut smart = SmartSchool::default(); + for fish in &school.fishes { + smart.add(*fish); + } + smart + } +} + +#[cfg(test)] +mod test { + use super::{Fish, School}; + + #[test] + fn default() { + assert_eq!(Fish::default().timer, 8); + } + + #[test] + fn spawn() { + let mut fish = Fish::default(); + for _ in 0..8 { + assert_eq!(fish.tick(), None); + } + assert_eq!(fish.tick(), Some(Fish::default())); + assert_eq!(fish.timer, 6); + } + + #[test] + fn school_growth() { + let mut school = School::default(); + school.add(Fish::new(0)); + school.add(Fish::new(0)); + school.add(Fish::new(0)); + assert_eq!(school.size(), 3); + school.tick(); + assert_eq!(school.size(), 6); + } +} diff --git a/src/day_06/parsing.rs b/src/day_06/parsing.rs new file mode 100644 index 0000000..55ffc06 --- /dev/null +++ b/src/day_06/parsing.rs @@ -0,0 +1,35 @@ +use super::model::{Fish, School}; +use crate::parsing::parse_number; +use yap::Tokens; + +pub fn parse_fish(tokens: &mut impl Tokens) -> Option { + parse_number(tokens).map(|num| Fish { timer: num }) +} + +pub fn parse_school_of_fish(tokens: &mut impl Tokens) -> School { + School { + fishes: tokens.sep_by(|t| parse_fish(t), |t| t.token(',')).collect(), + } +} + +#[cfg(test)] +mod test { + use super::super::model::Fish; + use yap::IntoTokens; + + #[test] + fn parse_fish() { + assert_eq!( + Some(Fish { timer: 1 }), + super::parse_fish(&mut "1".into_tokens()) + ); + } + + #[test] + fn parse_school() { + assert_eq!( + 5, + super::parse_school_of_fish(&mut "3,4,3,1,2".into_tokens()).size() + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index eedabef..34efd03 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,5 +7,6 @@ pub mod day_02; pub mod day_03; pub mod day_04; pub mod day_05; +pub mod day_06; aoc_lib! { year = 2021 }