Advent of Code 2021 - Day 17

This topic is about Day 17 of the Advent of Code 2021.

We have a private leaderboard (shared with users of Erlang Forums):

https://adventofcode.com/2021/leaderboard/private/view/370884

The entry code is:
370884-a6a71927

I used a brute-force solution, testing many more initial velocities than necessary. It does the job in less than 3 seconds on my computer.

1 Like

I think for part1, to find the best initial y velocity we can use something like y = abs(target_area.y.min) - 1 so that the max(y) is as big as possible and the probe doesn’t overshoot the target area when falling.

I got this idea after drawing the example with initial velocity of 6,9:

.....................#.........
.....................#.........
...............................
.....................#.........
...............................
...............................
.....................#.........
...............................
...............................
...............................
....................##.........
...............................
...............................
...............................
...............................
..................#..#.........
...............................
...............................
...............................
...............................
...............................
...............#.....#.........
...............................
...............................
...............................
...............................
...............................
...............................
...........#.........#.........
...............................
...............................
...............................
...............................
...............................
...............................
...............................
......#..............#.........
...............................
...............................
...............................
...............................
...............................
...............................
...............................
...............................
S....................#.........
...............................
...............................
...............................
...............................
....................TTTTTTTTTTT
....................TTTTTTTTTTT
....................TTTTTTTTTTT
....................TTTTTTTTTTT
....................TTTTTTTTTTT
....................T#TTTTTTTTT
1 Like

Yes, your formula for y works for part 1 with my input. My input is:

target area: x=185..221, y=-122..-74

and the best initial velocity is {19, 121}.

1 Like
1 Like

Here’s my solution.
I’m not sure if this is the best approach, but using streams to determine the trajectory turned out pretty nice imho.
Runs in 2.5s for both parts on my machine.

Part 1 can be solved with a simple approach.

target_y_range = -85..-56
max_vy = -1 - target_y_range.first
div(max_vy * (max_vy + 1), 2)

My full solution for day 17.

3 Likes

I did the same thing for part 1 :smiley:

def solve_part1([_x1, _x2, y1, _y2]), do: div(y1 * (y1 + 1), 2)

y2021/day_17.ex

2 Likes

A bit of brute force shooting, the only mathy thing happening is that the max height is calculated from the initial vertical velocity. For shits & giggles I also made a version that shoots every projectile on its own thread. I was shocked to find that was not even faster! :smile:

Right, so I somehow absolutely did not want to brute force, so instead I implemented a thing where

  1. I get the list of x velocity candidates (which contains a tuple of {x, min_steps, stays_in_target_area?, max_steps}
  2. get a similar list of y velocity candidates ({y, min_steps, max_steps})
  3. effectively double that list of y velocity candidates because I only went for y < 0 (and all the positive ones are can be calculated from these)

and then for part one sort the ys, and get the first one for which we can find an x with corresponding steps

now for part two I ended up doing two approaches (which at least on this input data end up being equivalent performance wise)

  1. for each y velocity find all x velocities, that reach the target in same amount of steps
  2. make a map of %{nr_of_steps => %{ x: [list, of, x, velocities, in, this, amount, of, steps], y: [list, of, y, velocities, ...] }, ...}, and then do a Cartesian product of all the xes and ys

Anyhow, here’s the code for the curious: aoc/day-17.ex at 3bf7bf357a7431f427c0a0647dc163aeba73fc38 · ramuuns/aoc · GitHub

My approach is kinda brute force but it still runs in under 1 second on my laptop. The way I wrote part 1 I had all the target hits already, so part two was just swapping a count in for where I was getting the max.

Random question - Is there a cleaner/better way to do a ternary for assignment than this?

highest_peak = if y > highest_peak, do: y, else: highest_peak

Coming from a JavaScript/TypeScript background I’m used to doing something like:

highestPeak = y > highestPeak ? y : highestPeak

I’m just wondering if there is a terse syntax like this I can use in Elixir.

y > highestPeak && y || highestPeak

1 Like

Thank you! I knew there had to be a way.

You may also use Kernel.max/2 for the same purpose:

highest_peak = max(highest_peak, y)
2 Likes
def part_2 do
  target = parse_input()

  vels =
    for x <- 0..target.x_range.last,
        y <- target.y_range.first..-target.y_range.first do
      {x, y}
    end

  Enum.count(vels, &valid?(&1, target))
end

Runs in 52ms. Full solution here

Definitely feels like I’m missing something, but brute force worked well enough on my input: