Skip to content

Commit

Permalink
Day 6
Browse files Browse the repository at this point in the history
  • Loading branch information
kcaffrey committed Dec 6, 2023
1 parent 46d261d commit f38a0ef
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 5 deletions.
11 changes: 6 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ Solutions for [Advent of Code](https://adventofcode.com/) in [Rust](https://www.

| Day | Part 1 | Part 2 |
| :---: | :---: | :---: |
| [Day 1](./src/bin/01.rs) | `39.5µs` | `80.3µs` |
| [Day 2](./src/bin/02.rs) | `43.9µs` | `43.0µs` |
| [Day 3](./src/bin/03.rs) | `117.0µs` | `112.6µs` |
| [Day 4](./src/bin/04.rs) | `52.3µs` | `55.7µs` |
| [Day 5](./src/bin/05.rs) | `23.5µs` | `26.9µs` |
| [Day 1](./src/bin/01.rs) | `37.5µs` | `80.3µs` |
| [Day 2](./src/bin/02.rs) | `43.8µs` | `42.8µs` |
| [Day 3](./src/bin/03.rs) | `115.0µs` | `113.1µs` |
| [Day 4](./src/bin/04.rs) | `52.4µs` | `55.1µs` |
| [Day 5](./src/bin/05.rs) | `23.4µs` | `27.1µs` |
| [Day 6](./src/bin/06.rs) | `193.0ns` | `105.0ns` |

**Total: 0.59ms**
<!--- benchmarking table --->
Expand Down
2 changes: 2 additions & 0 deletions data/examples/06.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Time: 7 15 30
Distance: 9 40 200
134 changes: 134 additions & 0 deletions src/bin/06.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use std::str::FromStr;

advent_of_code::solution!(6);

pub fn part_one(input: &str) -> Option<u64> {
let races = input.parse::<Races>().expect("input should parse");
races
.races
.into_iter()
.map(Race::number_of_ways_to_beat)
.reduce(|acc, n| acc * n)
}

pub fn part_two(input: &str) -> Option<u64> {
let (time_str, distance_str) = input.split_once('\n')?;
let time = time_str
.split_once(':')?
.1
.chars()
.filter_map(|ch| ch.to_digit(10).map(u64::from))
.reduce(|acc, d| acc * 10 + d)?;
let distance = distance_str
.split_once(':')?
.1
.chars()
.filter_map(|ch| ch.to_digit(10).map(u64::from))
.reduce(|acc, d| acc * 10 + d)?;
let race = Race {
time,
best_distance: distance,
};
Some(race.number_of_ways_to_beat())
}

#[derive(Debug, Clone, PartialEq, Eq)]
struct Races {
races: Vec<Race>,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
struct Race {
time: u64,
best_distance: u64,
}

impl Race {
pub fn number_of_ways_to_beat(self) -> u64 {
// If we charge for charge_time (charge_time <= race_time), then
// the velocity (v) at the end will be v = charge_time.
// We then have (race_time - charge_time) left in the race,
// and distance = v * t = charge_time * (race_time - charge_time).
//
// We want to solve for distance > best_distance. If we solve for equality,
// we get charge_time^2 - race_time*charge_time + best_distance = 0.
//
// Solving for charge_time using the quadratic formula results in
// charge_time = 0.5 * (race_time +/- sqrt(race_time^2 - 4*best_distance))
// We can compute these two bounds (taking the floor of the upper bound and ceiling
// of the lower bound), check the value for the distance at those two points,
// and if necessary shrink the range. This should then result in our answer:
// upper_bound - lower_bound + 1
//
let rt = self.time as f64;
let bd = self.best_distance as f64;
let mut lower_bound = (0.5 * (rt - (rt * rt - 4. * bd).sqrt())).ceil().max(0.) as u64;
let mut upper_bound = (0.5 * (rt + (rt * rt - 4. * bd).sqrt())).floor().min(rt) as u64;
if self.calculate_distance(lower_bound) <= self.best_distance {
lower_bound += 1;
}
if self.calculate_distance(upper_bound) <= self.best_distance {
upper_bound -= 1;
}
if upper_bound < lower_bound {
return 0;
}
upper_bound - lower_bound + 1
}

pub fn calculate_distance(self, charge_time: u64) -> u64 {
// See above
charge_time * (self.time - charge_time)
}
}

#[derive(Debug)]
struct ParseRacesErr;

impl FromStr for Races {
type Err = ParseRacesErr;

fn from_str(s: &str) -> Result<Self, Self::Err> {
let (times_str, distances_str) = s.split_once('\n').ok_or(ParseRacesErr)?;
let times = times_str
.split_once(':')
.ok_or(ParseRacesErr)?
.1
.split_whitespace()
.map(|s| s.parse::<u64>().map_err(|_| ParseRacesErr));
let distances = distances_str
.split_once(':')
.ok_or(ParseRacesErr)?
.1
.split_whitespace()
.map(|s| s.parse::<u64>().map_err(|_| ParseRacesErr));
Ok(Self {
races: times
.zip(distances)
.map(|(t, d)| {
Ok(Race {
time: t?,
best_distance: d?,
})
})
.collect::<Result<Vec<_>, ParseRacesErr>>()?,
})
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_part_one() {
let result = part_one(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(288));
}

#[test]
fn test_part_two() {
let result = part_two(&advent_of_code::template::read_file("examples", DAY));
assert_eq!(result, Some(71503));
}
}

0 comments on commit f38a0ef

Please sign in to comment.