diff --git a/2021/day17_trick_shot/Cargo.toml b/2021/day17_trick_shot/Cargo.toml new file mode 100644 index 0000000..9f2db7f --- /dev/null +++ b/2021/day17_trick_shot/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "day17_trick_shot" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] diff --git a/2021/day17_trick_shot/challenge.txt b/2021/day17_trick_shot/challenge.txt new file mode 100644 index 0000000..84f5a7b --- /dev/null +++ b/2021/day17_trick_shot/challenge.txt @@ -0,0 +1,123 @@ +You finally decode the Elves' message. `HI`, the message says. You continue searching for the sleigh keys. + +Ahead of you is what appears to be a large [ocean trench](https://en.wikipedia.org/wiki/Oceanic_trench). Could the keys have fallen into it? You'd better send a probe to investigate. + +The probe launcher on your submarine can fire the probe with any [integer](https://en.wikipedia.org/wiki/Integer) velocity in the `x` (forward) and `y` (upward, or downward if negative) directions. For example, an initial `x,y` velocity like `0,10` would fire the probe straight up, while an initial velocity like `10,-1` would fire the probe forward at a slight downward angle. + +The probe's `x,y` position starts at `0,0`. Then, it will follow some trajectory by moving in *steps*. On each step, these changes occur in the following order: + +* The probe's `x` position increases by its `x` velocity. +* The probe's `y` position increases by its `y` velocity. +* Due to drag, the probe's `x` velocity changes by `1` toward the value `0`; that is, it decreases by `1` if it is greater than `0`, increases by `1` if it is less than `0`, or does not change if it is already `0`. +* Due to gravity, the probe's `y` velocity decreases by `1`. + +For the probe to successfully make it into the trench, the probe must be on some trajectory that causes it to be within a *target area* after any step. The submarine computer has already calculated this target area (your puzzle input). For example: + +``` +target area: x=20..30, y=-10..-5 +``` + +This target area means that you need to find initial `x,y` velocity values such that after any step, the probe's `x` position is at least `20` and at most `30`, *and* the probe's `y` position is at least `-10` and at most `-5`. + +Given this target area, one initial velocity that causes the probe to be within the target area after any step is `7,2`: + +``` +.............#....#............ +.......#..............#........ +............................... +S........................#..... +............................... +............................... +...........................#... +............................... +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTT#TT +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTTTTT + +``` + +In this diagram, `S` is the probe's initial position, `0,0`. The `x` coordinate increases to the right, and the `y` coordinate increases upward. In the bottom right, positions that are within the target area are shown as `T`. After each step (until the target area is reached), the position of the probe is marked with `#`. (The bottom-right `#` is both a position the probe reaches and a position in the target area.) + +Another initial velocity that causes the probe to be within the target area after any step is `6,3`: + +``` +...............#..#............ +...........#........#.......... +............................... +......#..............#......... +............................... +............................... +S....................#......... +............................... +............................... +............................... +.....................#......... +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................T#TTTTTTTTT +....................TTTTTTTTTTT + +``` + +Another one is `9,0`: + +``` +S........#..................... +.................#............. +............................... +........................#...... +............................... +....................TTTTTTTTTTT +....................TTTTTTTTTT# +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTTTTT +....................TTTTTTTTTTT + +``` + +One initial velocity that *doesn't* cause the probe to be within the target area after any step is `17,-4`: + +``` +S.............................................................. +............................................................... +............................................................... +............................................................... +.................#............................................. +....................TTTTTTTTTTT................................ +....................TTTTTTTTTTT................................ +....................TTTTTTTTTTT................................ +....................TTTTTTTTTTT................................ +....................TTTTTTTTTTT..#............................. +....................TTTTTTTTTTT................................ +............................................................... +............................................................... +............................................................... +............................................................... +................................................#.............. +............................................................... +............................................................... +............................................................... +............................................................... +............................................................... +............................................................... +..............................................................# + +``` + +The probe appears to pass through the target area, but is never within it after any step. Instead, it continues down and to the right - only the first few steps are shown. + +If you're going to fire a highly scientific probe out of a super cool probe launcher, you might as well do it with *style*. How high can you make the probe go while still reaching the target area? + +In the above example, using an initial velocity of `6,9` is the best you can do, causing the probe to reach a maximum `y` position of `*45*`. (Any higher initial `y` velocity causes the probe to overshoot the target area entirely.) + +Find the initial velocity that causes the probe to reach the highest `y` position and still eventually be within the target area after any step. *What is the highest `y` position it reaches on this trajectory?* + +To begin, [get your puzzle input](17/input). + +Answer: \ No newline at end of file diff --git a/2021/day17_trick_shot/day17_trick_shot-f4.core b/2021/day17_trick_shot/day17_trick_shot-f4.core new file mode 100644 index 0000000..100e7e9 Binary files /dev/null and b/2021/day17_trick_shot/day17_trick_shot-f4.core differ diff --git a/2021/day17_trick_shot/src/lib.rs b/2021/day17_trick_shot/src/lib.rs new file mode 100644 index 0000000..20752dc --- /dev/null +++ b/2021/day17_trick_shot/src/lib.rs @@ -0,0 +1,123 @@ +use core::fmt::Display; +use std::num::ParseIntError; + +#[derive(Debug, PartialEq, Eq)] +pub enum ParseError { + ParseIntError(std::num::ParseIntError), + LineMalformed(String), +} + +impl From for ParseError { + fn from(value: ParseIntError) -> Self { + Self::ParseIntError(value) + } +} + +impl Display for ParseError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ParseIntError(e) => write!(f, "Unable to parse into integer: {e}"), + Self::LineMalformed(v) => write!(f, "Line is malformed: {v}"), + } + } +} + +struct Area { + x_min: isize, + x_max: isize, + y_min: isize, + y_max: isize, +} + +impl TryFrom<&str> for Area { + type Error = ParseError; + + fn try_from(value: &str) -> Result { + let components: Vec<_> = value.split(&[' ', ',']).collect(); + if components.len() == 5 { + let x: Vec<_> = components[2].split(&['.', '=']).collect(); + let y: Vec<_> = components[4].split(&['.', '=']).collect(); + if x.len() == 4 && y.len() == 4 { + Ok(Self { + x_min: x[1].parse()?, + x_max: x[3].parse()?, + y_min: y[1].parse()?, + y_max: y[3].parse()?, + }) + } else { + Err(Self::Error::LineMalformed(value.to_string())) + } + } else { + Err(Self::Error::LineMalformed(value.to_string())) + } + } +} + +pub fn run(input: &str) -> Result<(isize, usize), ParseError> { + let target = Area::try_from(input)?; + let mut max_y = 0; + let mut hits = 0; + for y in target.y_min..-target.y_min { + for x in 1..=target.x_max { + let attempt = launch((0, 0), (x, y), &target, 0); + if attempt.0 { + max_y = max_y.max(attempt.1); + hits += 1; + } + } + } + let first = max_y; + let second = hits; + Ok((first, second)) +} + +fn launch((x, y): (isize, isize), (x_vel, y_vel): (isize, isize), target: &Area, max_y: isize) -> (bool, isize) { + if (target.x_min..=target.x_max).contains(&x) && (target.y_min..=target.y_max).contains(&y) { + (true, max_y) + } else if x_vel >= 0 && target.x_max < x || + x_vel <= 0 && target.x_min > x || + y_vel <= 0 && target.y_min > y { + (false, 0) + } else if x_vel > 0 { + launch((x+x_vel, y+y_vel), (x_vel-1, y_vel-1), target, max_y.max(y+y_vel)) + } else if x_vel < 0 { + launch((x+x_vel, y+y_vel), (x_vel+1, y_vel-1), target, max_y.max(y+y_vel)) + } else { + launch((x+x_vel, y+y_vel), (0, y_vel-1), target, max_y.max(y+y_vel)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::fs::read_to_string; + + fn read_file(name: &str) -> String { + read_to_string(name).expect(&format!("Unable to read file: {name}")[..]).trim().to_string() + } + + #[test] + fn test_sample() { + let sample_input = read_file("tests/sample_input"); + let target = Area::try_from(&sample_input[..]).unwrap(); + let should_hit = [ + (7, 2), + (6, 3), + (9, 0), + ]; + let should_miss = [ (17, -4) ]; + for vel in should_hit { + assert!(launch((0, 0), vel, &target, 0).0); + } + for vel in should_miss { + assert!(!launch((0, 0), vel, &target, 0).0); + } + assert_eq!(run(&sample_input), Ok((45, 112))); + } + + #[test] + fn test_challenge() { + let challenge_input = read_file("tests/challenge_input"); + assert_eq!(run(&challenge_input), Ok((6903, 2351))); + } +} diff --git a/2021/day17_trick_shot/tests/challenge_input b/2021/day17_trick_shot/tests/challenge_input new file mode 100644 index 0000000..bd61515 --- /dev/null +++ b/2021/day17_trick_shot/tests/challenge_input @@ -0,0 +1 @@ +target area: x=235..259, y=-118..-62 diff --git a/2021/day17_trick_shot/tests/sample_input b/2021/day17_trick_shot/tests/sample_input new file mode 100644 index 0000000..a07e02d --- /dev/null +++ b/2021/day17_trick_shot/tests/sample_input @@ -0,0 +1 @@ +target area: x=20..30, y=-10..-5