diff --git a/input/2023/day6.txt b/input/2023/day6.txt new file mode 100644 index 0000000..377a67c --- /dev/null +++ b/input/2023/day6.txt @@ -0,0 +1,2 @@ +Time: 46 68 98 66 +Distance: 358 1054 1807 1080 diff --git a/src/day06.rs b/src/day06.rs new file mode 100644 index 0000000..4510a22 --- /dev/null +++ b/src/day06.rs @@ -0,0 +1,93 @@ +use aoc_runner_derive::{aoc_generator, aoc}; +use nom::{sequence::{preceded, separated_pair, pair}, bytes::complete::tag, IResult, character::complete::{multispace0, line_ending}, multi::separated_list0}; + + +fn parse_number_list(start_tag: &str) -> impl Fn(&str) -> IResult<&str, Vec> + '_ { + move |input| { + preceded(pair(tag(start_tag), multispace0), separated_list0(pair(tag(" "), multispace0), nom::character::complete::u64))(input) + } +} + +fn parse_input(input: &str) -> IResult<&str, (Vec, Vec)> { + separated_pair(parse_number_list("Time:"), line_ending, parse_number_list("Distance:"))(input) +} + +#[aoc_generator(day6, part1)] +fn input_generator_part1(input: &str) -> (Vec, Vec) { + parse_input(input).unwrap().1 +} + +#[aoc_generator(day6, part2)] +fn input_generator_part2(input: &str) -> (u64, u64) { + let mut lines = input.split('\n'); + let time_line = lines.next().unwrap(); + let dist_line = lines.next().unwrap(); + let time_line = time_line.replace(' ', ""); + let dist_line = dist_line.replace(' ', ""); + let time = time_line[5..].parse().unwrap(); + let dist = dist_line[9..].parse().unwrap(); + (time, dist) +} + + +fn race_dist(speed: u64, total_time: u64) -> u64 { + speed*(total_time-speed) +} + +// let n be the number of ms you hold the button +// let t be the race time +// let d be the record distance +// time units is ms unless specified, distance is mm unless specified +// n(t-n)>d +// nt-n^2>d +// nt-n^2-d>0 +// +// nt-n^2-d=0 +// -n^2+tn-d=0 +// +// n=-t(+-)sqrt(t^2-4*1*-d)/2 +// n=-t(+-)sqrt(t^2+4d)/2 +// +// Let the two values be n_1 and n_2 +// +// d/dn(nt-n^2-d)=t-2n +// d/dn(t-2n)=-2 +// +// Function is always concave down +// Concave down parabolas are >0 between their roots +// +// Range of winning times are ceil(n_1) to floor(n_2) +// Excluding an n value if distance is = record distance +fn ways_to_win_race(time: u64, dist: u64) -> u64 { + let f_time = time as f64; + let f_dist = dist as f64; + let sqr_discr = ((f_time * f_time) - (4.0 * f_dist)).sqrt(); + let n_1 = (((-f_time) + sqr_discr)/(-2.0)).ceil() as u64; + let n_2 = (((-f_time) - sqr_discr)/(-2.0)).floor() as u64; + let (n_1, n_2) = if n_1 > n_2 { + (n_2, n_1) + } else { + (n_1, n_2) + }; + let excl_n1 = race_dist(n_1, time) == dist; + let excl_n2 = race_dist(n_2, time) == dist; + let total_offset = match (excl_n1, excl_n2) { + (false, false) => 0, + (false, true) => 1, + (true, false) => 1, + (true, true) => 2, + }; + ((n_2 - n_1) + 1) - total_offset +} + +#[aoc(day6, part1)] +fn part1((times, dists): &(Vec, Vec)) -> u64 { + times.iter().zip(dists.iter()).map(|(&time, &dist)| { + ways_to_win_race(time, dist) + }).product() +} + +#[aoc(day6, part2)] +fn part2(&(time, dist): &(u64, u64)) -> u64 { + ways_to_win_race(time, dist) +} diff --git a/src/lib.rs b/src/lib.rs index bad81fe..4d0b2b6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,5 +5,6 @@ mod day02; mod day03; mod day04; mod day05; +mod day06; aoc_lib! { year = 2023 }