aoc-rs-2021/src/day_17/model.rs

191 lines
5.2 KiB
Rust
Raw Normal View History

use std::ops::RangeBounds;
use itertools::Itertools;
#[derive(PartialEq, Eq, Debug)]
pub struct TargetArea(pub(crate) (isize, isize), pub(crate) (isize, isize));
#[derive(PartialEq, Eq)]
pub enum State {
EnRoute,
InTargetArea,
Overshot,
}
impl TargetArea {
pub fn get_position_state(&self, position: (isize, isize)) -> State {
let x_bounds_en_route = self.get_bounds(true, true);
let y_bounds_en_route = self.get_bounds(false, true);
let x_bounds_target = self.get_bounds(true, false);
let y_bounds_target = self.get_bounds(false, false);
match (
x_bounds_en_route.contains(&position.0),
y_bounds_en_route.contains(&position.1),
x_bounds_target.contains(&position.0),
y_bounds_target.contains(&position.1),
) {
(_, _, true, true) => State::InTargetArea,
(true, true, _, _) => State::EnRoute,
_ => State::Overshot,
}
}
pub fn get_bounds(&self, x: bool, en_route: bool) -> impl RangeBounds<isize> {
let mut values = if x {
vec![self.0 .0, self.0 .1]
} else {
vec![self.1 .0, self.1 .1]
};
if en_route && x {
values.push(0);
} else if en_route && !x {
values.push(isize::MAX);
}
*values.iter().min().unwrap()..=*values.iter().max().unwrap()
}
pub fn get_lowest_possible_x_velocity(&self) -> Option<isize> {
let bounds = self.get_bounds(true, false);
for i in 1..=Ord::max(self.0 .0, self.0 .1) {
if XLoc(0, i).any(|loc| bounds.contains(&loc)) {
return Some(i);
}
}
None
}
pub fn get_highest_possible_y_velocity(&self) -> Option<isize> {
let bounds = self.get_bounds(false, false);
let y_min = Ord::min(self.1 .0, self.1 .1);
(y_min..500)
.rev()
.find(|i| YLoc(0, *i, y_min).any(|loc| bounds.contains(&loc)))
}
pub fn get_all_possible_velocities(&self) -> Vec<(isize, isize)> {
let x_bounds = self.get_bounds(true, false);
let x_velocities = (1..=Ord::max(self.0 .0, self.0 .1))
.filter(|i| XLoc(0, *i).any(|loc| x_bounds.contains(&loc)));
let y_bounds = self.get_bounds(false, false);
let y_min = Ord::min(self.1 .0, self.1 .1);
let y_velocities = (y_min..500)
.rev()
.filter(|i| YLoc(0, *i, y_min).any(|loc| y_bounds.contains(&loc)));
let mut ret_val = Vec::new();
x_velocities
.cartesian_product(y_velocities)
.filter(|velocity| Probe::new(*velocity).will_reach_target_area(self))
.for_each(|velocity| ret_val.push(velocity));
ret_val
}
}
pub struct XLoc(isize, isize);
impl Iterator for XLoc {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
let velocity = self.1;
self.0 += velocity;
let item = self.0;
match velocity {
n if n > 0 => self.1 -= 1,
n if n < 0 => self.1 += 1,
_ => return None,
};
Some(item)
}
}
pub struct YLoc(isize, isize, isize);
impl Iterator for YLoc {
type Item = isize;
fn next(&mut self) -> Option<Self::Item> {
let velocity = self.1;
self.0 += velocity;
self.1 -= 1;
let item = self.0;
if item < self.2 {
None
} else {
Some(item)
}
}
}
#[derive(Clone, Debug)]
pub struct Probe {
pub position: (isize, isize),
pub velocity: (isize, isize),
}
impl Probe {
pub fn new(velocity: (isize, isize)) -> Self {
Self {
position: (0, 0),
velocity,
}
}
pub fn step(&mut self) {
self.position.0 += self.velocity.0;
self.position.1 += self.velocity.1;
self.velocity.0 = match self.velocity.0 {
n if n > 0 => self.velocity.0 - 1,
n if n < 0 => self.velocity.0 + 1,
_ => 0,
};
self.velocity.1 -= 1;
}
pub fn will_reach_target_area(&self, target_area: &TargetArea) -> bool {
let mut probe = self.clone();
while target_area.get_position_state(probe.position) == State::EnRoute {
probe.step();
}
matches!(
target_area.get_position_state(probe.position),
State::InTargetArea
)
}
}
#[cfg(test)]
mod test {
use crate::day_17::model::Probe;
use super::TargetArea;
fn example() -> TargetArea {
TargetArea((20, 30), (-10, -5))
}
#[test]
fn examples() {
for velocity in [(7, 2), (6, 3), (9, 0), (6, 9)] {
assert!(Probe::new(velocity).will_reach_target_area(&example()));
}
for velocity in [(17, -4)] {
assert!(!Probe::new(velocity).will_reach_target_area(&example()));
}
for y_vel in 10..1000 {
assert!(!Probe::new((6, y_vel)).will_reach_target_area(&example()));
}
}
#[test]
fn lowest_possible_x_velocity() {
assert_eq!(example().get_lowest_possible_x_velocity(), Some(6));
}
#[test]
fn highest_possible_y_velocity() {
assert_eq!(example().get_highest_possible_y_velocity(), Some(9));
}
}