Powered by AppSignal & Oban Pro

Day 9: Rope Bridge

2022/day09.livemd

Day 9: Rope Bridge

Mix.install([:kino])

Section

input = Kino.Input.textarea("Input")
moves =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn move ->
    move
    |> String.split(" ")
    |> then(fn [m, d] ->
      [m, String.to_integer(d)]
    end)
  end)
  |> Stream.transform([], &{apply(List, :duplicate, &1), &2})
defmodule Rope do
  def new(knots \\ 2) do
    List.duplicate({0, 0}, knots)
  end

  def simulate_tail(moves, rope) do
    Enum.map_reduce(moves, rope, fn direction, rope ->
      direction
      |> Rope.move(rope)
      |> then(fn rope ->
        {List.last(rope), rope}
      end)
    end)
    |> elem(0)
  end

  defp move("U", [{x, y} | rope]) do
    move_tail([{x, y + 1} | rope])
  end

  defp move("D", [{x, y} | rope]) do
    move_tail([{x, y - 1} | rope])
  end

  defp move("L", [{x, y} | rope]) do
    move_tail([{x - 1, y} | rope])
  end

  defp move("R", [{x, y} | rope]) do
    move_tail([{x + 1, y} | rope])
  end

  defp move_tail([{x0, y0}, {x1, y1} | rope]) when abs(x0 - x1) == 2 and abs(y0 - y1) == 2 do
    [{x0, y0} | move_tail([{x0 - div(x0 - x1, 2), y0 - div(y0 - y1, 2)} | rope])]
  end

  defp move_tail([{x0, y}, {x1, y} | rope]) when abs(x0 - x1) == 2 do
    [{x0, y} | move_tail([{x0 - div(x0 - x1, 2), y} | rope])]
  end

  defp move_tail([{x0, y0}, {x1, _y1} | rope]) when abs(x0 - x1) == 2 do
    [{x0, y0} | move_tail([{x0 - div(x0 - x1, 2), y0} | rope])]
  end

  defp move_tail([{x, y0}, {x, y1} | rope]) when abs(y0 - y1) == 2 do
    [{x, y0} | move_tail([{x, y0 - div(y0 - y1, 2)} | rope])]
  end

  defp move_tail([{x0, y0}, {_x1, y1} | rope]) when abs(y0 - y1) == 2 do
    [{x0, y0} | move_tail([{x0, y0 - div(y0 - y1, 2)} | rope])]
  end

  defp move_tail(rope), do: rope
end

Part 1

Rope.simulate_tail(moves, Rope.new())
|> Enum.uniq()
|> Enum.count()

Part 2

Rope.simulate_tail(moves, Rope.new(10))
|> Enum.uniq()
|> Enum.count()