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

AoC: Day 9

2022/elixir/day9.livemd

AoC: Day 9

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"},
  {:complex, "~> 0.4"}
])

Setup

{:ok, data} = KinoAOC.download_puzzle("2022", "9", System.fetch_env!("LB_AOC_SECRET"))

Solve

defmodule Day9 do
  @dir %{R: {1, 0}, L: {-1, 0}, U: {0, 1}, D: {0, -1}}

  def solve(data, tsize) do
    dd = data |> String.split("\n", trim: true) |> Enum.map(&format/1)
    knots = Enum.map(1..tsize, fn _ -> %{x: 0, y: 0} end)

    {res, _} = Enum.reduce(dd, {MapSet.new(), knots}, &do_n_moves/2)
    MapSet.size(res)
  end

  def do_n_moves({mv, steps}, acc) do
    Enum.reduce(1..steps, acc, fn _, acc -> move_one(mv, acc) end)
  end

  def move_one(mv, {res, knots}) do
    [head | tails] = knots
    h = move_head(mv, head)
    {last_t, tails} = move_tails(tails, h)

    {MapSet.put(res, last_t), [h | tails]}
  end

  def move_head({sx, sy}, %{x: x, y: y}), do: %{x: x + sx, y: y + sy}

  def move_tails(tails, h) do
    {_, tails} =
      Enum.reduce(tails, {h, []}, fn t, {h, acc} ->
        t = move_tail(h, t)
        {t, [t | acc]}
      end)

    [t | _] = tails

    {t, Enum.reverse(tails)}
  end

  def move_tail(h, t) do
    dx = h.x - t.x
    dy = h.y - t.y

    case {abs(dx), abs(dy)} do
      {2, 0} ->
        %{x: t.x + step(dx), y: t.y}

      {0, 2} ->
        %{x: t.x, y: t.y + step(dy)}

      {ddx, ddy} when ddx == 2 or ddy == 2 ->
        %{x: t.x + step(dx), y: t.y + step(dy)}

      _ ->
        t
    end
  end

  def step(x) when x < 0, do: -1
  def step(_x), do: 1

  def format(row) do
    [dir, snum] = row |> String.split(" ", trim: true)
    {@dir[String.to_atom(dir)], String.to_integer(snum)}
  end
end

# {6391, 2593}
Day9.solve(data, 2) |> IO.inspect(label: "res1")
Day9.solve(data, 10) |> IO.inspect(label: "res2")
:ok

Test

ExUnit.start(autorun: false)

defmodule AoCTest do
  use ExUnit.Case, async: false

  @inp1 """
  R 4
  U 4
  L 3
  D 1
  R 4
  D 1
  L 5
  R 2
  """

  @inp2 """
  R 5
  U 8
  L 8
  D 3
  R 17
  D 10
  L 25
  U 20
  """

  test "solves first task" do
    assert 13 = Day9.solve(@inp1, 2)
  end

  test "solves second task" do
    assert 36 = Day9.solve(@inp2, 10)
  end
end

ExUnit.run()

Using Complex numbert to track coordinates

test = """
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2
"""

defmodule Coords do
  alias Complex, as: Cx
  @dirs %{R: Cx.new(1, 0), L: Cx.new(-1, 0), U: Cx.new(0, 1), D: Cx.new(0, -1)}

  def run(data) do
    moves = data |> String.split("\n", trim: true) |> Enum.map(&amp;fmt/1)
    head = Cx.new(0, 0) |> IO.inspect(label: "H")

    moves
    |> Enum.reduce(head, fn {k, steps} = el, acc ->
      @dirs[k]
      |> Complex.multiply(steps)
      |> Cx.add(acc)
      |> IO.inspect(label: "#{inspect(el)}")
    end)
  end

  def fmt(row) do
    [dir, snum] = row |> String.split(" ", trim: true)
    {String.to_atom(dir), String.to_integer(snum)}
  end
end

Coords.run(test)