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

Day 6

day6.livemd

Day 6

Setup

input =
  System.fetch_env!("LB_AOC_DIR")
  |> Path.join("data/day6.txt")
  |> File.read!()

nil
nil

Solve

defmodule Day6 do
  defp parse(input) do
    for {line, i} <- Enum.with_index(String.split(input, "\n")),
        line != "",
        {item, j} <- Enum.with_index(String.codepoints(line)),
        reduce: %{} do
      acc ->
        acc = Map.put(acc, {i, j}, item)
        if item == "^", do: Map.put(acc, :start, {i, j}), else: acc
    end
  end

  defp rotate(:up), do: :right
  defp rotate(:right), do: :down
  defp rotate(:down), do: :left
  defp rotate(:left), do: :up

  defp move({i, j}, :left), do: {i, j - 1}
  defp move({i, j}, :right), do: {i, j + 1}
  defp move({i, j}, :up), do: {i - 1, j}
  defp move({i, j}, :down), do: {i + 1, j}

  defp simulate(board) do
    Stream.unfold({Map.fetch!(board, :start), :up}, fn
      :halt ->
        nil

      {position, direction} ->
        new_position = move(position, direction)

        case Map.get(board, new_position) do
          "#" ->
            {nil, {position, rotate(direction)}}

          nop when nop in [".", "^"] ->
            {position, {new_position, direction}}

          _ ->
            {position, :halt}
        end
    end)
    |> Stream.filter(&amp; &amp;1)
  end

  @doc ~S"""
  iex> Day6.part1("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...\n")
  41
  """
  def part1(input) do
    input
    |> parse()
    |> simulate()
    |> Enum.into(MapSet.new())
    |> MapSet.size()
  end

  @doc ~S"""
  iex> Day6.part2("....#.....\n.........#\n..........\n..#.......\n.......#..\n..........\n.#..^.....\n........#.\n#.........\n......#...\n")
  6
  """
  def part2(input) do
    board = parse(input)

    for {pos, value} <- board, value != "#", value != "^", pos != :start do
      Map.put(board, pos, "#")
    end
    |> Task.async_stream(fn board -> simulate(board) |> Stream.run() end,
      on_timeout: :kill_task,
      timeout: 1_000
    )
    |> Stream.filter(&amp;match?({:exit, :timeout}, &amp;1))
    |> Enum.to_list()
    |> Enum.count()
  end
end
{:module, Day6, <<70, 79, 82, 49, 0, 0, 19, ...>>, {:part2, 1}}