diff --git a/2021/src/bin/day17.rs b/2021/src/bin/day17.rs new file mode 100644 index 0000000..1b31772 --- /dev/null +++ b/2021/src/bin/day17.rs @@ -0,0 +1,111 @@ +#![feature(test)] +extern crate test; +use std::ops::RangeInclusive; + +type TargetArea = (RangeInclusive, RangeInclusive); +type Probe = ((isize, isize), (isize, isize)); + +#[derive(Debug, PartialEq)] +enum ProbeStatus { + Hit, + Miss, + NoLongerReachable, +} + +fn calc_status((_, (x, y)): &Probe, (xtarget, ytarget): &TargetArea) -> ProbeStatus { + if xtarget.contains(x) && ytarget.contains(y) { + ProbeStatus::Hit + } else if y < ytarget.start() { + ProbeStatus::NoLongerReachable + } else { + ProbeStatus::Miss + } +} + +fn parse_input() -> TargetArea { + (34..=67, -215..=-186) +} + +fn step(((xvel, yvel), (x, y)): Probe) -> Probe { + ((xvel - xvel.signum(), yvel - 1), (x + xvel, y + yvel)) +} + +fn part1(target: &TargetArea, hits: &Vec<(isize, isize)>) -> isize { + hits.iter() + .map(|start_vel| { + std::iter::successors(Some((*start_vel, (0, 0))), |probe| { + let probe = step(*probe); + (calc_status(&probe, target) == ProbeStatus::Miss).then(|| probe) + }) + .map(|(_, (_, y))| y) + .max() + .unwrap() + }) + .max() + .unwrap() +} + +fn find_hits(target: &TargetArea) -> Vec<(isize, isize)> { + let mut hits = Vec::new(); + for xstart in -1000..1000 { + for ystart in -1000..1000 { + let mut probe = ((xstart, ystart), (0, 0)); + loop { + probe = step(probe); + match calc_status(&probe, target) { + ProbeStatus::Hit => { + hits.push((xstart, ystart)); + break; + } + ProbeStatus::Miss => (), + ProbeStatus::NoLongerReachable => break, + } + } + } + } + hits +} + +fn part2(hits: &Vec<(isize, isize)>) -> usize { + hits.len() +} + +fn main() { + let target = parse_input(); + let hits = find_hits(&target); + println!("Part 1: {}", part1(&target, &hits)); + println!("Part 2: {}", part2(&hits)); +} + +#[cfg(test)] +mod tests { + use super::*; + use aoc2021::*; + + #[test] + fn part1_test() { + let input = (20..=30, -10..=-5); + let hits = find_hits(&input); + assert_eq!(part1(&input, &hits), 45); + } + + #[test] + fn part2_test() { + let input = (20..=30, -10..=-5); + let hits = find_hits(&input); + assert_eq!(part2(&hits), 112); + } + + #[bench] + fn bench_find_hits(b: &mut test::Bencher) { + let input = parse_input(); + b.iter(|| assert_eq!(find_hits(&input).len(), 2040)) + } + + #[bench] + fn bench_part1(b: &mut test::Bencher) { + let input = parse_input(); + let hits = find_hits(&input); + b.iter(|| assert_eq!(part1(&input, &hits), 23005)) + } +}