Solve day 6
This commit is contained in:
parent
c458652c7a
commit
e7f641678a
3 changed files with 101 additions and 0 deletions
2
input/06-test
Normal file
2
input/06-test
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
Time: 7 15 30
|
||||||
|
Distance: 9 40 200
|
71
src/bin/06.rs
Normal file
71
src/bin/06.rs
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
use aoc2023::{concat, parse_ws_separated};
|
||||||
|
use itertools::Itertools;
|
||||||
|
use std::ops::RangeInclusive;
|
||||||
|
|
||||||
|
const INPUT: &str = include_str!("../../input/06");
|
||||||
|
const TEST_INPUT: &str = include_str!("../../input/06-test");
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
assert_eq!(part1(TEST_INPUT), 288);
|
||||||
|
println!("Part 1: {}", part1(INPUT));
|
||||||
|
assert_eq!(part2(TEST_INPUT), 71503);
|
||||||
|
println!("Part 2: {}", part2(INPUT));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part1(input: &str) -> u64 {
|
||||||
|
parse(input).map(how_to_beat).map(len).product()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn part2(input: &str) -> u64 {
|
||||||
|
let race = parse(input).reduce(Race::concat).unwrap();
|
||||||
|
let range = how_to_beat(race);
|
||||||
|
len(range)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Range of time the button might be pressed to beat a record.
|
||||||
|
fn how_to_beat(race: Race) -> RangeInclusive<u64> {
|
||||||
|
let time = race.time as f64;
|
||||||
|
let record = race.record as f64;
|
||||||
|
|
||||||
|
// The distance travelled d is dependant on the time t the button is pressed: d = time*t - t^2.
|
||||||
|
// Solve d = record for min and max t:
|
||||||
|
let offset = time * 0.5;
|
||||||
|
let add = ((-time * 0.5).powf(2.0) - record).sqrt();
|
||||||
|
let min = offset - add;
|
||||||
|
let max = offset + add;
|
||||||
|
|
||||||
|
// Shrink the interval to only return times to beat the record.
|
||||||
|
let min = min.floor() as u64 + 1;
|
||||||
|
let max = max.ceil() as u64 - 1;
|
||||||
|
|
||||||
|
min..=max
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(range: RangeInclusive<u64>) -> u64 {
|
||||||
|
range.try_len().unwrap() as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Race {
|
||||||
|
time: u64,
|
||||||
|
record: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Race {
|
||||||
|
fn concat(self, other: Self) -> Self {
|
||||||
|
Self {
|
||||||
|
time: concat(self.time, other.time),
|
||||||
|
record: concat(self.record, other.record),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse(input: &str) -> impl Iterator<Item = Race> + '_ {
|
||||||
|
let mut lines = input.lines();
|
||||||
|
let mut parse_next_line =
|
||||||
|
|prefix: &str| parse_ws_separated(lines.next().unwrap().trim_start_matches(prefix));
|
||||||
|
let times = parse_next_line("Time: ");
|
||||||
|
let records = parse_next_line("Distance: ");
|
||||||
|
times
|
||||||
|
.zip(records)
|
||||||
|
.map(|(time, record)| Race { time, record })
|
||||||
|
}
|
28
src/lib.rs
28
src/lib.rs
|
@ -1 +1,29 @@
|
||||||
|
use std::fmt::{Debug, Display};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// Parse a whitespace separated list of things.
|
||||||
|
///
|
||||||
|
/// Panics on parse error.
|
||||||
|
pub fn parse_ws_separated<T>(s: &str) -> impl Iterator<Item = T> + '_
|
||||||
|
where
|
||||||
|
T: FromStr,
|
||||||
|
<T as FromStr>::Err: Debug,
|
||||||
|
{
|
||||||
|
s.split_ascii_whitespace().map(|s| s.parse().unwrap())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Concatenate two things.
|
||||||
|
///
|
||||||
|
/// Panics if the concatenation is not be parseable.
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// # use aoc2023::concat;
|
||||||
|
/// assert_eq!(concat(123, 456), 123456);
|
||||||
|
/// ```
|
||||||
|
pub fn concat<T>(a: T, b: T) -> T
|
||||||
|
where
|
||||||
|
T: Display + FromStr,
|
||||||
|
<T as FromStr>::Err: Debug,
|
||||||
|
{
|
||||||
|
format!("{a}{b}").parse().unwrap()
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue