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

Day 2: Dive!

2021/day02.livemd

Day 2: Dive!

Section

defmodule Setup do
  ## parsing the input, converting it to a list of lines
  def get_input(prompt) do
    case IO.gets(prompt) do
      :eof -> ""
      line -> line <> get_input(prompt)
    end
  end

  def parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&amp;parse_line/1)
  end

  defp parse_line("forward " <> units), do: {:forward, String.to_integer(units)}
  defp parse_line("down " <> units), do: {:down, String.to_integer(units)}
  defp parse_line("up " <> units), do: {:up, String.to_integer(units)}
end

input =
  Setup.get_input("Input")
  |> Setup.parse_input()
defmodule Submarine do
  def position(instructions, pos \\ {0, 0}) do
    {x, y} = Enum.reduce(instructions, pos, &amp;move/2)
    x * y
  end

  defp move({:forward, unit}, {x, y}), do: {x + unit, y}
  defp move({:down, unit}, {x, y}), do: {x, y + unit}
  defp move({:up, unit}, {x, y}), do: {x, y - unit}
end
Submarine.position(input)

Part 2

defmodule Submarine2 do
  def position(instructions, pos \\ {0, 0, 0}) do
    {_, x, y} = Enum.reduce(instructions, pos, &amp;move/2)
    x * y
  end

  def move({:forward, unit}, {a, x, y}), do: {a, x + unit, y + a * unit}
  def move({:down, unit}, {a, x, y}), do: {a + unit, x, y}
  def move({:up, unit}, {a, x, y}), do: {a - unit, x, y}
end
Submarine2.position(input)

Test

ExUnit.start()

defmodule Test do
  use ExUnit.Case

  setup do
    {:ok,
     input:
       Setup.parse_input("""
       forward 5
       down 5
       forward 8
       up 3
       down 8
       forward 2
       """)}
  end

  test "setup", %{input: input} do
    assert input ==
             [
               forward: 5,
               down: 5,
               forward: 8,
               up: 3,
               down: 8,
               forward: 2
             ]
  end

  test "position", %{input: input} do
    assert Submarine.position(input) == 150
  end

  test "position2", %{input: input} do
    assert Submarine2.position(input) == 900
  end
end

ExUnit.run()

Animate descent

Mix.install([:vega_lite, :kino])

alias VegaLite, as: Vl
widget =
  Vl.new(width: 400, height: 400)
  |> Vl.mark(:line)
  |> Vl.encode_field(:x, "position", type: :quantitative)
  |> Vl.encode_field(:y, "depth", type: :quantitative)
  |> Kino.VegaLite.new()
  |> tap(&amp;Kino.render/1)

Enum.reduce(input, {0, 0, 0}, fn instr, pos ->
  {_, x, y} = pos = Submarine2.move(instr, pos)

  point = %{"position" => x, "depth" => -y}
  Kino.VegaLite.push(widget, point, window: 2000)
  Process.sleep(25)

  pos
end)