aoc/year2023/day06.rs
1//! # Wait For It
2//!
3//! We can solve analytically using the quadratic formula.
4//! * `x` is time spent holding the button.
5//! * `t` is the duration of the race.
6//! * `d` is the record distance.
7//!
8//! Then the distance travelled is:
9//!
10//! * `x * (t - x)`
11//!
12//! To beat the record the following condition must hold:
13//!
14//! * `x * (t - x) = d`
15//! * `x² - tx +d = 0`
16//!
17//! The start and end times where we will be the record are given by the roots of the
18//! quadratic equation which we can solve using the
19//! [quadratic formula](https://en.wikipedia.org/wiki/Quadratic_formula).
20//!
21//! * `(t ± √(t² - 4d)) / 2`
22use crate::util::parse::*;
23
24pub fn parse(input: &str) -> Vec<&str> {
25 input.lines().collect()
26}
27
28pub fn part1(input: &[&str]) -> u128 {
29 race(input[0], input[1])
30}
31
32pub fn part2(input: &[&str]) -> u128 {
33 race(&merge(input[0]), &merge(input[1]))
34}
35
36fn merge(line: &str) -> String {
37 line.chars().filter(char::is_ascii_digit).collect()
38}
39
40fn race(first: &str, second: &str) -> u128 {
41 first
42 .iter_unsigned::<u128>()
43 .zip(second.iter_unsigned::<u128>())
44 .map(|(time, distance)| {
45 // Use the quadratic formula to find the start and end positions.
46 let root = (time * time - 4 * distance).isqrt();
47 let mut start = (time - root).div_ceil(2);
48 let mut end = time.midpoint(root);
49
50 // As we're using integer math we may need to adjust 1 step.
51 if start * (time - start) > distance {
52 start -= 1;
53 }
54 if end * (time - end) > distance {
55 end += 1;
56 }
57
58 end - start - 1
59 })
60 .product()
61}