advent-of-code/2023/day6/src/main.rs

166 lines
4.0 KiB
Rust

const FILE_STR: &str = include_str!("input.txt");
#[derive(Debug, Default, Copy, Clone)]
struct RaceRecord {
time: usize,
distance: usize,
}
#[derive(Debug, Default)]
struct Races {
records: Vec<RaceRecord>,
}
impl Races {
fn parse(records: &str) -> Self {
let mut lines = records
.split('\n')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.split(':'));
let times: Vec<_> = lines
.next()
.unwrap()
.nth(1)
.unwrap()
.split_whitespace()
.map(|n| n.trim())
.filter(|n| !n.is_empty())
.map(|n| n.parse::<usize>().unwrap())
.collect();
let distances: Vec<_> = lines
.next()
.unwrap()
.nth(1)
.unwrap()
.split_whitespace()
.map(|n| n.trim())
.filter(|n| !n.is_empty())
.map(|n| n.parse::<usize>().unwrap())
.collect();
let mut r = Races::default();
assert_eq!(times.len(), distances.len());
for i in 0..times.len() {
r.records.push(RaceRecord {
time: times[i],
distance: distances[i],
})
}
r
}
fn parse_single(records: &str) -> Self {
let mut lines = records
.split('\n')
.map(|s| s.trim())
.filter(|s| !s.is_empty())
.map(|s| s.split(':'));
let time = lines
.next()
.unwrap()
.nth(1)
.unwrap()
.replace(' ', "")
.parse::<usize>()
.unwrap();
let distance = lines
.next()
.unwrap()
.nth(1)
.unwrap()
.replace(' ', "")
.parse::<usize>()
.unwrap();
Races {
records: Vec::from([RaceRecord { time, distance }]),
}
}
fn calculate_winning_counts(&self) -> Vec<usize> {
self.records
.iter()
.map(|r| winning_possibilities(*r).len())
.collect()
}
fn calculate_win_count(&self) -> usize {
winning_possibilities(self.records[0]).len()
}
fn calculate_possible_win_product(&self) -> usize {
self.calculate_winning_counts()
.into_iter()
.reduce(|prev, curr| prev * curr)
.unwrap()
}
}
fn winning_possibilities(race: RaceRecord) -> Vec<usize> {
let distance_calculation =
|race_length: usize, button_time: usize| button_time * (race_length - button_time);
let possible_range = 1..race.time;
possible_range
.filter_map(|button_time| {
let res = distance_calculation(race.time, button_time);
if res > race.distance {
Some(res)
} else {
None
}
})
.collect()
}
fn part_one() {
let records = Races::parse(FILE_STR);
println!(
"Part 1 Product of winning possibilities: {}",
records.calculate_possible_win_product()
);
}
fn part_two() {
let record = Races::parse_single(FILE_STR);
println!("{:#?}", record);
println!(
"Part 2 winning possibilities: {}",
record.calculate_win_count()
);
}
fn main() {
part_one();
part_two();
}
#[cfg(test)]
mod tests {
const EXAMPLE_FILE_STR: &'static str = include_str!("example-input.txt");
use super::*;
#[test]
fn test_calculate_winning_counts() {
let races = Races::parse(EXAMPLE_FILE_STR);
assert_eq!(Vec::from([4, 8, 9]), races.calculate_winning_counts());
}
#[test]
fn test_calculate_possible_win_product() {
let races = Races::parse(EXAMPLE_FILE_STR);
assert_eq!(288, races.calculate_possible_win_product());
}
#[test]
fn test_calculate_win_count() {
let races = Races::parse_single(EXAMPLE_FILE_STR);
assert_eq!(71503, races.calculate_win_count());
}
}