Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 17

lib/day17.livemd

Day 17

Setup

sample = "target area: x=20..30, y=-10..-5"

input = "target area: x=241..275, y=-75..-49"

Part a

defmodule Day17 do
  def shoot(x_vol, y_vol, x_range, y_range), do: shoot(0, 0, x_vol, y_vol, x_range, y_range, 0)

  def shoot(x, y, x_vol, y_vol, x_range, y_range, y_max) do
    # IO.puts("Probe at: #{x}, #{y}")
    {x, y, x_vol, y_vol} = step(x, y, x_vol, y_vol)
    y_max = max(y, y_max)

    case {hit?(x, y, x_range, y_range), missed?(x, y, x_range, y_range)} do
      {true, _} -> {:hit, y_max}
      {_, true} -> :missed
      _ -> shoot(x, y, x_vol, y_vol, x_range, y_range, y_max)
    end
  end

  def step(x, y, x_vol, y_vol) do
    x = x + x_vol
    y = y + y_vol

    x_vol =
      case x_vol do
        n when n > 0 -> n - 1
        n when n < 0 -> n + 1
        _ -> 0
      end

    y_vol = y_vol - 1
    {x, y, x_vol, y_vol}
  end

  def hit?(x, y, {x_min, x_max}, {y_min, y_max}) do
    x_min <= x and
      x <= x_max and
      y_min <= y and
      y <= y_max
  end

  def missed?(x, y, {_x_min, x_max}, {y_min, _y_max}) do
    x > x_max or y < y_min
  end
end

"target area: " <> area = input

ranges =
  area
  |> String.split(", ", trim: true)
  |> Enum.map(fn command -> String.slice(command, 2..-1) end)

x_range =
  ranges
  |> Enum.at(0)
  |> String.split("..")
  |> Enum.map(&amp;String.to_integer/1)
  |> List.to_tuple()

{x_min, x_max} = x_range

y_range =
  ranges
  |> Enum.at(1)
  |> String.split("..")
  |> Enum.map(&amp;String.to_integer/1)
  |> List.to_tuple()

{y_min, y_max} = y_range

for x <- 0..x_max,
    y <- y_min..100 do
  Day17.shoot(x, y, x_range, y_range)
end
|> Enum.reject(&amp;(&amp;1 == :missed))
|> Enum.max_by(&amp;elem(&amp;1, 1))
|> elem(1)

Part b

for x <- 0..x_max,
    y <- y_min..100 do
  Day17.shoot(x, y, x_range, y_range)
end
|> Enum.reject(&amp;(&amp;1 == :missed))
|> Enum.count()