Advent of code 2021: Day 17

This article is part of a series where I'll be diving head first into the Advent of code 2021. I'll be documenting the challenge of solving such a puzzle and how I got to the answer. I want to prefix this by stating that I can't cheat for any of these challenges; with that, I mean I can't look up any other implementations online.

In this article I'll be solving: Advent of code 2021: Day #17.

Part 1

My submarine escaped from the caves and now has to throw a probe into an ocean trench. The probe has to reach a certain target area, which is the puzzle input. Considering that the puzzle input is this small, I’ll skip the file reads and just use the input directly inside the code. The probe always starts at (0, 0) and can be shot with different velocities (xvel,yvel). The velocity decreases due to drag and gravity. The puzzle wants to know what the maximum height is the probe can launch for your puzzle input.

This was remarkably easy. I started out by writing a method called probe() which takes four arguments:

Following all the rules for drag and gravity, the method turned out like this:

use std::ops::RangeInclusive;

type Target = RangeInclusive<i16>;

fn probe(
    mut x_vel: i16,
    mut y_vel: i16,
    target_x: &Target,
    target_y: &Target,
) -> Option<i16> {
    let (mut x, mut y) = (0, 0);
    let mut max_y = 0;

    loop {
        x += x_vel;
        y += y_vel;

        // drag
        if x > 0 && x_vel > 0 {
            x_vel -= 1;
        } else if x < 0 {
            x_vel += 1;
        }

        y_vel -= 1; // gravity

        if y > max_y {
            max_y = y;
        }

        if &x > target_x.end() || &y < target_y.start() {
            break None
        }

        if target_x.contains(&x) && target_y.contains(&y) {
            break Some(max_y)
        }
    }
}

Nothing too hard. The next part is to determine which “shot” makes the y-value go the highest:

fn max_probe(target_x: &Target, target_y: &Target) -> i16 {
    let end = *target_x.end();
    let mut max = 0;

    for x in 1..end {
        for y in 1..end {
            if let Some(max_p) = probe(x, y, &target_x, &target_y) {
                if max_p > m {
                    max = max_p;
                }
            }
        }
    }

    max
}

Done!

Part 2

The next part is to determine how many possible shots there are which land inside the target. This was also not too hard:

fn num_probes(target_x: &Target, target_y: &Target) -> i16 {
    let max = *target_x.end();
    let mut count = 0;

    for x in 1..=max {
        for y in *target_y.start()..=max {
            let p = probe(x, y, &target_x, &target_y);
            if p.is_some() {
                count += 1;
            }
        }
    }

    count
}

I had to play with the loop ranges a little, but nothing too out of the ordinary. It got me to the correct answer.

The full solution is available on GitHub.

1716151413121110987654321