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

Day9

day09.livemd

Day9

Mix.install([
  {:kino, "~> 0.8.0"}
])

Input

input = Kino.Input.textarea("input")
moves =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    [move, count] = String.split(line, " ", trim: true)

    {move, String.to_integer(count)}
  end)
defmodule RopeSimulator do
  def tail_visited_coord(moves, knot_number) do
    knots = Enum.map(0..(knot_number - 1), fn _ -> {0, 0} end)

    Enum.reduce(moves, {knots, []}, fn {move, count}, {knots, visited} ->
      Enum.reduce(0..(count - 1), {knots, visited}, fn _, {knots, visited} ->
        head = Enum.at(knots, 0)

        head =
          case move do
            "U" -> add(head, {0, -1})
            "D" -> add(head, {0, 1})
            "L" -> add(head, {-1, 0})
            "R" -> add(head, {1, 0})
          end

        knots =
          knots
          |> List.update_at(0, fn _ -> head end)
          |> update_following_knots(knot_number)

        {knots, [Enum.at(knots, knot_number - 1) | visited]}
      end)
    end)
    |> elem(1)
    |> Enum.uniq()
  end

  defp update_following_knots(knots, knot_number) do
    Enum.reduce(1..(knot_number - 1), knots, fn i, knots ->
      prev = Enum.at(knots, i - 1)
      current = Enum.at(knots, i)

      {diff_x, diff_y} = diff = diff(prev, current)

      if abs(diff_x) <= 1 and abs(diff_y) <= 1 do
        knots
      else
        move = normalized(diff)

        List.update_at(knots, i, fn _ -> add(current, move) end)
      end
    end)
  end

  defp add({a_x, a_y}, {b_x, b_y}) do
    {a_x + b_x, a_y + b_y}
  end

  defp diff({a_x, a_y}, {b_x, b_y}) do
    {a_x - b_x, a_y - b_y}
  end

  defp normalized({x, y}) do
    {normalized(x), normalized(y)}
  end

  defp normalized(v) when v < 0, do: -1
  defp normalized(v) when v > 0, do: 1
  defp normalized(_), do: 0
end

Part 1

RopeSimulator.tail_visited_coord(moves, 2) |> Enum.count()

Part 2

RopeSimulator.tail_visited_coord(moves, 10) |> Enum.count()