Powered by AppSignal & Oban Pro

Day 09

2022/day09.livemd

Day 09

Section

defmodule Day09 do
  def part1(input) do
    input
    |> Enum.reduce({List.duplicate({0, 0}, 2), [{0, 0}]}, fn instruction, {knots, acc} ->
      move(instruction, knots, acc)
    end)
    |> elem(1)
    |> Enum.uniq()
    |> length()
  end

  def part2(input) do
    input
    |> Enum.reduce({List.duplicate({0, 0}, 10), [{0, 0}]}, fn instruction, {knots, acc} ->
      move(instruction, knots, acc)
    end)
    |> elem(1)
    |> Enum.uniq()
    |> length()
  end

  defp move({_, 0}, rope, acc), do: {rope, acc}

  defp move({direction, dist}, [h | t], acc) do
    h = move_once(h, direction)

    rev_rope =
      for knot <- t, reduce: [h] do
        [prev | _] = state -> [follow(knot, prev) | state]
      end

    move({direction, dist - 1}, Enum.reverse(rev_rope), [hd(rev_rope) | acc])
  end

  defp move_once({x, y}, "U"), do: {x, y + 1}
  defp move_once({x, y}, "D"), do: {x, y - 1}
  defp move_once({x, y}, "L"), do: {x - 1, y}
  defp move_once({x, y}, "R"), do: {x + 1, y}

  defp follow({xt, yt}, {xh, yh}) when abs(xt - xh) <= 1 and abs(yt - yh) <= 1 do
    {xt, yt}
  end

  defp follow({xt, yt}, {xh, yh}) do
    {xt - sign(xt - xh), yt - sign(yt - yh)}
  end

  defp sign(0), do: 0
  defp sign(n), do: n |> abs() |> div(n)
end
input =
  "#{__DIR__}/day09.txt"
  |> File.stream!()
  |> Stream.map(&String.trim/1)
  |> Stream.map(&String.split/1)
  |> Stream.map(fn [direction, distance] -> {direction, String.to_integer(distance)} end)
Day09.part1(input)
Day09.part2(input)