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",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "day06"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"aoc_libs",
|
||||||
|
"nom",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "memchr"
|
||||||
version = "2.6.4"
|
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