mod parsing; mod race_record; type NumericValue = u64; pub fn solve_task_1(input: &str) -> String { let parsed = parsing::parsing(input); let mut how_many_times_won = 1; for next_record in parsed { how_many_times_won *= all_rounds_of_durations(next_record.time()) .filter(|&distance| distance > next_record.reached_distance()) .count() as NumericValue; } how_many_times_won.to_string() } pub fn solve_task_2(input: &str) -> String { let parsed = parsing::parsing_part_2(input); let time_to_beat = parsed.time(); assert!(time_to_beat > 1, "Race time must be at least 2"); const FIRST_AND_LAST_CYCLE_DO_NOT_MOVE: NumericValue = 2; const HALF_AT_OR_OVER_PEAK: NumericValue = 2; let range: NumericValue = (parsed.time() - FIRST_AND_LAST_CYCLE_DO_NOT_MOVE) / HALF_AT_OR_OVER_PEAK; const FIRST_CYCLE_NO_DISTANCE: usize = 1; for (index, next_distance) in all_rounds_of_durations(parsed.time()) .enumerate() .skip(FIRST_CYCLE_NO_DISTANCE) { if next_distance > parsed.reached_distance() { const EVEN_TIME_TO_BEAT_LEADS_UNEVEN_PICK: NumericValue = 1; let one_offset = if time_to_beat % 2 == 0 { EVEN_TIME_TO_BEAT_LEADS_UNEVEN_PICK } else { 0 }; let lower_than_distance_over_pick = (index as NumericValue) - 1; let both_halves = (range - lower_than_distance_over_pick) * 2; let how_many_times_won = both_halves + one_offset; return how_many_times_won.to_string(); } } unreachable!(); } fn all_rounds_of_durations(duration: NumericValue) -> impl Iterator { let limit = duration as usize; std::iter::repeat(duration) .take(limit) .enumerate() .map(|(speed_duration, duration)| { simuluate_reached_distance_at(duration, speed_duration as NumericValue) }) } fn simuluate_reached_distance_at( duration: NumericValue, speed_duration: NumericValue, ) -> NumericValue { assert!( duration >= speed_duration, "Duration {:?}. Speed Duration: {:?}", duration, speed_duration ); let rest_time = duration - speed_duration; rest_time * speed_duration } #[cfg(test)] mod testing { use crate::day6::{simuluate_reached_distance_at, solve_task_2, NumericValue}; use super::solve_task_1; #[test] fn should_simuluate_reached_distance_at() { fn assert_case( duration: NumericValue, speed_duration: NumericValue, expected: NumericValue, ) { let actual = simuluate_reached_distance_at(duration, speed_duration); assert_eq!( expected, actual, "Duration: {}\n Speed Duration: {}", duration, speed_duration ); } const DURATION: NumericValue = 7; for (speed_duration, expected) in [ (0, 0), (1, 6), (2, 10), (3, 12), (4, 12), (5, 10), (6, 6), (7, 0), ] { assert_case(DURATION, speed_duration, expected) } } #[test] fn should_solve_task_1() { let input = include_str!("day6/day_example_input.txt"); let actual = solve_task_1(input); let expected = "288"; assert_eq!(expected, actual); } #[test] fn should_solve_task_2() { let input = include_str!("day6/day_example_input.txt"); let actual = solve_task_2(input); let expected = "71503"; assert_eq!(expected, actual); } }