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 conditition 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 let times = first.iter_unsigned::<u128>();
42 let distances = second.iter_unsigned::<u128>();
43 let mut result = 1;
44
45 for (time, distance) in times.zip(distances) {
46 // Use the quadratic formula to find the start and end positions.
47 let root = (time * time - 4 * distance).isqrt();
48 let mut start = (time - root).div_ceil(2);
49 let mut end = (time + root) / 2;
50
51 // As we're using integer math we may need to adjust 1 step.
52 if start * (time - start) > distance {
53 start -= 1;
54 }
55 if end * (time - end) > distance {
56 end += 1;
57 }
58
59 result *= end - start - 1;
60 }
61
62 result
63}