day 06.
This commit is contained in:
parent
8bd03989a8
commit
e1d0849bba
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -55,6 +55,14 @@ dependencies = [
|
|||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "day06"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aoc_libs",
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.6.4"
|
||||
|
|
10
days/day06/Cargo.toml
Normal file
10
days/day06/Cargo.toml
Normal file
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "day06"
|
||||
authors.workspace = true
|
||||
description.workspace = true
|
||||
version.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
[dependencies]
|
||||
aoc_libs.workspace = true
|
||||
nom.workspace=true
|
2
days/day06/src/input.txt
Normal file
2
days/day06/src/input.txt
Normal file
|
@ -0,0 +1,2 @@
|
|||
Time: 40 81 77 72
|
||||
Distance: 219 1012 1365 1089
|
15
days/day06/src/main.rs
Normal file
15
days/day06/src/main.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
mod part1;
|
||||
mod part2;
|
||||
mod parse;
|
||||
|
||||
fn main() {
|
||||
let input = include_str!("./input.txt");
|
||||
let structured_input = parse::parse(input);
|
||||
|
||||
println!("Part One");
|
||||
println!("Result: {}", part1::part1(&structured_input));
|
||||
|
||||
let structured_input = parse::part2_parse(input);
|
||||
println!("Part Two");
|
||||
println!("Result: {}", part2::part2(structured_input));
|
||||
}
|
161
days/day06/src/parse.rs
Normal file
161
days/day06/src/parse.rs
Normal file
|
@ -0,0 +1,161 @@
|
|||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::multispace0,
|
||||
multi::separated_list1,
|
||||
sequence::{preceded, terminated},
|
||||
IResult,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub struct Race {
|
||||
pub time: u64,
|
||||
pub record: u64,
|
||||
}
|
||||
|
||||
impl Race {
|
||||
pub fn distance_given_charge_time(&self, charge_time: u64) -> u64 {
|
||||
if charge_time <= self.time {
|
||||
charge_time * (self.time - charge_time)
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn num_ways_to_win(&self) -> u64 {
|
||||
// since distance = charge(time-charge),
|
||||
// we can rearrange into charge^2-(time)charge + distance = 0.
|
||||
// if we set distance to record+1 (the min distance needed to win), we can use the quadratic formula, where
|
||||
// a=1
|
||||
// also notice that the upper and lower bound of charge times always sums up to
|
||||
// the total time, so we can compute the lower bound from the upper bound.
|
||||
// (too lazy to prove this...)
|
||||
let b = -(self.time as f64);
|
||||
let c = (self.record + 1) as f64;
|
||||
let upper_bound = ((-b + (b.powi(2) - 4.0 * c).sqrt()) / 2.0).floor() as u64;
|
||||
let lower_bound = self.time - upper_bound;
|
||||
println!(
|
||||
"upper bound is {}, lower bound is {}",
|
||||
upper_bound, lower_bound
|
||||
);
|
||||
// off by one because if your upper and lower bounds are the same, there is 1 way to win.
|
||||
upper_bound - lower_bound + 1
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse(input: &str) -> Vec<Race> {
|
||||
let times: IResult<&str, Vec<u64>> = terminated(
|
||||
preceded(
|
||||
preceded(tag("Time:"), multispace0),
|
||||
separated_list1(multispace0, nom::character::complete::u64),
|
||||
),
|
||||
multispace0,
|
||||
)(input);
|
||||
let (input, times) = times.unwrap();
|
||||
let distances: IResult<&str, Vec<u64>> = terminated(
|
||||
preceded(
|
||||
preceded(tag("Distance:"), multispace0),
|
||||
separated_list1(multispace0, nom::character::complete::u64),
|
||||
),
|
||||
multispace0,
|
||||
)(input);
|
||||
let (input, distances) = distances.unwrap();
|
||||
assert_eq!(input, "");
|
||||
times
|
||||
.into_iter()
|
||||
.zip(distances)
|
||||
.map(|r| Race {
|
||||
time: r.0,
|
||||
record: r.1,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn part2_parse(input: &str) -> Race {
|
||||
let mut string = input.to_string();
|
||||
string.retain(|c| c != ' ');
|
||||
let time: IResult<&str, u64> = terminated(
|
||||
preceded(tag("Time:"), nom::character::complete::u64),
|
||||
multispace0,
|
||||
)(&string);
|
||||
let (input, time) = time.unwrap();
|
||||
let distance: IResult<&str, u64> = terminated(
|
||||
preceded(tag("Distance:"), nom::character::complete::u64),
|
||||
multispace0,
|
||||
)(input);
|
||||
let (input, distance) = distance.unwrap();
|
||||
assert_eq!(input, "");
|
||||
Race {
|
||||
time,
|
||||
record: distance,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_num_ways_to_win() {
|
||||
let input = Race { time: 7, record: 9 };
|
||||
assert_eq!(input.num_ways_to_win(), 4);
|
||||
let input = Race {
|
||||
time: 15,
|
||||
record: 40,
|
||||
};
|
||||
assert_eq!(input.num_ways_to_win(), 8);
|
||||
let input = Race {
|
||||
time: 30,
|
||||
record: 200,
|
||||
};
|
||||
assert_eq!(input.num_ways_to_win(), 9);
|
||||
let input = Race {
|
||||
time: 71530,
|
||||
record: 940200,
|
||||
};
|
||||
assert_eq!(input.num_ways_to_win(), 71503);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_distance_given_charge_time() {
|
||||
let input = Race { time: 7, record: 9 };
|
||||
assert_eq!(input.distance_given_charge_time(0), 0);
|
||||
assert_eq!(input.distance_given_charge_time(1), 6);
|
||||
assert_eq!(input.distance_given_charge_time(2), 10);
|
||||
assert_eq!(input.distance_given_charge_time(3), 12);
|
||||
assert_eq!(input.distance_given_charge_time(4), 12);
|
||||
assert_eq!(input.distance_given_charge_time(5), 10);
|
||||
assert_eq!(input.distance_given_charge_time(6), 6);
|
||||
assert_eq!(input.distance_given_charge_time(7), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_part2() {
|
||||
let input = concat!("Time: 7 15 30\n", "Distance: 9 40 200\n",);
|
||||
assert_eq!(
|
||||
part2_parse(input),
|
||||
Race {
|
||||
time: 71530,
|
||||
record: 940200
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let input = concat!("Time: 7 15 30\n", "Distance: 9 40 200\n",);
|
||||
assert_eq!(
|
||||
parse(input),
|
||||
vec![
|
||||
Race { time: 7, record: 9 },
|
||||
Race {
|
||||
time: 15,
|
||||
record: 40
|
||||
},
|
||||
Race {
|
||||
time: 30,
|
||||
record: 200
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
26
days/day06/src/part1.rs
Normal file
26
days/day06/src/part1.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use crate::parse::*;
|
||||
|
||||
pub fn part1(input: &[Race]) -> u64 {
|
||||
input.iter().map(|r| r.num_ways_to_win()).product()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_part1() {
|
||||
let input = vec![
|
||||
Race { time: 7, record: 9 },
|
||||
Race {
|
||||
time: 15,
|
||||
record: 40,
|
||||
},
|
||||
Race {
|
||||
time: 30,
|
||||
record: 200,
|
||||
},
|
||||
];
|
||||
assert_eq!(part1(&input), 288);
|
||||
}
|
||||
}
|
16
days/day06/src/part2.rs
Normal file
16
days/day06/src/part2.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use crate::parse::*;
|
||||
|
||||
pub fn part2(input: Race) -> u64 {
|
||||
input.num_ways_to_win()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_part2() {
|
||||
let input = Race{ time: 71530, record: 940200 };
|
||||
assert_eq!(part2(input), 71503);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue