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

Advent of Code 2022 - Day 9

day09.livemd

Advent of Code 2022 - Day 9

Day 9: Rope Bridge

Description

Utils

defmodule Load do
  def file(path) do
    File.read!(__DIR__ <> path)
    |> String.split("\n", trim: true)
  end

  def string(value) do
    value |> String.split("\n", trim: true)
  end
end

Input

input = Load.file("/data/day09.txt")

Part 1

Simulate your complete hypothetical series of motions. How many positions does the tail of the rope visit at least once?

defmodule Part1 do
  def head_positions(input) do
    input
    |> move_head([{0, 0}])
    |> Enum.reverse()
  end

  def move_head([], head_positions) do
    head_positions
  end

  def move_head([instruction | input], head_positions) do
    [dir, steps] = instruction |> String.split(" ", trim: true)

    head = hd(head_positions)

    direction =
      case dir do
        "R" -> {1, 0}
        "L" -> {-1, 0}
        "U" -> {0, 1}
        "D" -> {0, -1}
      end

    steps = steps |> String.to_integer()

    new_head_positions =
      1..steps
      |> Enum.map(fn step ->
        {elem(head, 0) + step * elem(direction, 0), elem(head, 1) + step * elem(direction, 1)}
      end)
      |> Enum.reverse()

    move_head(input, new_head_positions ++ head_positions)
  end

  def follow([], tail_positions) do
    tail_positions
  end

  def follow([head_position | head_positions], [tail_position | _] = tail_positions) do
    vd = vertical_distance(head_position, tail_position)
    hd = horizontal_distance(head_position, tail_position)

    if vd > 1 or hd > 1 do
      offset = {
        horizontal_offset(head_position, tail_position),
        vertical_offset(head_position, tail_position)
      }

      normalized_offset = normalize(offset)

      new_tail_position = {
        elem(tail_position, 0) + elem(normalized_offset, 0),
        elem(tail_position, 1) + elem(normalized_offset, 1)
      }

      follow(head_positions, [new_tail_position | tail_positions])
    else
      follow(head_positions, tail_positions)
    end
  end

  defp vertical_offset(pos1, pos2) do
    elem(pos1, 1) - elem(pos2, 1)
  end

  defp vertical_distance(pos1, pos2) do
    abs(vertical_offset(pos1, pos2))
  end

  defp horizontal_offset(pos1, pos2) do
    elem(pos1, 0) - elem(pos2, 0)
  end

  defp horizontal_distance(pos1, pos2) do
    abs(horizontal_offset(pos1, pos2))
  end

  defp normalize(pos) do
    x =
      case elem(pos, 0) do
        0 -> 0
        n -> (n / abs(n)) |> round()
      end

    y =
      case elem(pos, 1) do
        0 -> 0
        n -> (n / abs(n)) |> round()
      end

    {x, y}
  end
end

input
|> Part1.head_positions()
|> Part1.follow([{0, 0}])
|> Enum.uniq()
|> Enum.count()

Result

5883

Part 2

Simulate your complete series of motions on a larger rope with ten knots. How many positions does the tail of the rope visit at least once?

# Derp
input
|> Part1.head_positions()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.reverse()
|> Part1.follow([{0, 0}])
|> Enum.uniq()
|> Enum.count()

Result

2367