AoC2021/src/day17.rs

105 lines
2.8 KiB
Rust

use std::cmp::Ordering;
use crate::Day;
pub struct Day17 {
x_min: i32,
x_max: i32,
y_min: i32,
y_max: i32,
}
impl Day for Day17 {
fn init(content: String) -> anyhow::Result<Self> {
let c = content.split(' ').skip(1).collect::<Vec<_>>();
let (x_min, x_max) = c[1][2..(c[1].len() - 1)].split_once("..").unwrap();
let (y_min, y_max) = c[2][2..].split_once("..").unwrap();
Ok(Day17 {
x_min: x_min.parse()?,
x_max: x_max.parse()?,
y_min: y_min.parse()?,
y_max: y_max.parse()?,
})
}
fn part1(&self) -> anyhow::Result<String> {
let mut hits = Vec::new();
for dx in 0..=self.x_max {
for dy in -100..250 {
let points = self.calc((dx, dy));
if self.hits(&points) {
hits.push(max_height(&points).unwrap());
}
}
}
Ok(format!("{}", hits.iter().max().unwrap()))
}
fn part2(&self) -> anyhow::Result<String> {
let mut hits = Vec::new();
for dx in 0..=self.x_max {
for dy in -100..250 {
let points = self.calc((dx, dy));
if self.hits(&points) {
hits.push((dx, dy));
}
}
}
Ok(format!("{}", hits.len()))
}
}
impl Day17 {
fn hits(&self, points: &[(i32, i32)]) -> bool {
points.iter().any(|&(x, y)| x >= self.x_min && x <= self.x_max && y >= self.y_min && y <= self.y_max)
}
fn calc(&self, initial_velocity: (i32, i32)) -> Vec<(i32, i32)> {
let mut steps = vec![(0, 0)];
let mut velocity = initial_velocity;
loop {
let (x, y) = steps.last().unwrap();
let (x, y) = (x + velocity.0, y + velocity.1);
if x > self.x_max || y < self.y_min {
break;
}
steps.push((x, y));
match velocity.0.cmp(&0) {
Ordering::Less => velocity.0 += 1,
Ordering::Equal => {}
Ordering::Greater => velocity.0 -= 1,
}
velocity.1 -= 1;
}
steps
}
}
fn max_height(points: &[(i32, i32)]) -> Option<i32> {
points.iter().map(|&x| x.1).max()
}
#[cfg(test)]
mod tests {
use crate::{Day, day_tests};
use crate::day17::Day17;
const INPUT: &str = "target area: x=20..30, y=-10..-5";
#[test]
fn part1_test() -> anyhow::Result<()> {
let d = Day17::init(INPUT.to_string())?;
assert_eq!("45", d.part1()?);
Ok(())
}
#[test]
fn part2_test() -> anyhow::Result<()> {
let d = Day17::init(INPUT.to_string())?;
assert_eq!("112", d.part2()?);
Ok(())
}
day_tests!(Day17, "17", "4186", "2709");
}