use crate::{Problem, Solution}; pub struct Day06; impl Problem for Day06 { const DAY: u8 = 6; const INPUT: &'static str = include_str!("../input/day_06.txt"); } impl Solution for Day06 { type Answer1 = usize; type Answer2 = usize; fn part_1(input: &str) -> anyhow::Result { let (time, dist) = input.split_once('\n').expect("Invalid input lines"); let races = time .split_whitespace() .skip(1) .zip(dist.split_whitespace().skip(1)) .map(TryFrom::try_from) .try_collect::>()?; races .iter() .map(Race::wins) .reduce(|acc, n| acc * n) .ok_or(anyhow::format_err!("Failed to find race")) } fn part_2(input: &str) -> anyhow::Result { let race: Race = input.parse()?; Ok(race.wins()) } } #[derive(Debug)] struct Race { time: usize, dist: usize, } impl Race { fn run(&self, speed: usize) -> bool { (speed * (self.time - speed)) > self.dist } fn wins(&self) -> usize { let mut count = 0; for speed in 1..self.time { if self.run(speed) { count += 1; } else if count > 0 { break; } } count } } impl TryFrom<(&str, &str)> for Race { type Error = std::num::ParseIntError; fn try_from((time, dist): (&str, &str)) -> Result { Ok(Self { time: time.parse()?, dist: dist.parse()?, }) } } impl std::str::FromStr for Race { type Err = anyhow::Error; fn from_str(s: &str) -> Result { let (time, dist) = s .trim() .split_once('\n') .ok_or(anyhow::format_err!("Invalid race format"))?; let time = time .strip_prefix("Time: ") .ok_or(anyhow::format_err!("Invalid time format"))? .chars() .filter(|c| *c != ' ') .collect::() .trim() .parse()?; let dist = dist .strip_prefix("Distance: ") .ok_or(anyhow::format_err!("Invalid distance format"))? .chars() .filter(|c| *c != ' ') .collect::() .trim() .parse()?; Ok(Race { time, dist }) } } #[cfg(test)] mod tests { use super::*; const INPUT: &str = indoc::indoc! {" Time: 7 15 30 Distance: 9 40 200 "}; #[test] fn test_part_1() -> anyhow::Result<()> { Ok(assert_eq!(288, Day06::part_1(INPUT)?)) } #[test] fn test_part_2() -> anyhow::Result<()> { Ok(assert_eq!(71503, Day06::part_2(INPUT)?)) } }