aoc-rs-2021/src/day_17/model.rs
Jan Christian Grünhage 580f6259bc implement day 17
this is horrible, I'm not proud of it. There surely is some mathematical
feature that allows a smarter solution, but after I've not been able to
find that for a while, I've decided to just brute force it. Due to that,
I've not had any motivation to finish this solution on time or to remove
the loads of redundant code I've written here..
2021-12-18 18:10:16 +01:00

191 lines
5.2 KiB
Rust

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));
}
}