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

Day 6

livebook/6.livemd

Day 6

Part 1

# input = File.stream!("6/example.txt")

input = File.stream!("6/input.txt")
defmodule U do
  def parse(input) do
    {_, obstacles, init_pos} = Enum.reduce(input, {0, MapSet.new(), nil}, fn line, {i, obstacles, init_pos} ->
      {_, obstacles, init_pos} = Enum.reduce(String.codepoints(line), {0, obstacles, init_pos}, 
        fn char, {j, obstacles, init_pos} ->
          case char do
            "." -> {j + 1, obstacles, init_pos}
            "#" -> {j + 1, MapSet.put(obstacles, {i, j}), init_pos}
            "^" -> {j + 1, obstacles, {i, j, :up}}
            _ -> {j + 1, obstacles, init_pos}
          end
      end)
      {i + 1, obstacles, init_pos}
    end)
    size = {(Enum.at(input, 0) |> String.codepoints() |> Enum.count()) - 1, input |> Enum.to_list |> Enum.count()}
    {init_pos, obstacles, size}
  end
end
{pos, obstacles, size} = input |> U.parse
defmodule P1 do
  def step(pos, obstacles, size) do
    step(pos, MapSet.new(), obstacles, size)
  end

  defp step({i, j, dir}, path, obstacles, {w, h} = size) do
    path = MapSet.put(path, {i, j})
    {i_next, j_next} =
      case dir do
        :up -> {i - 1, j}
        :right -> {i, j + 1}
        :down -> {i + 1, j}
        :left -> {i, j - 1}
      end

    cond do
      i_next < 0 || i_next >= h || j_next < 0 || j_next >= w ->
        path

      MapSet.member?(obstacles, {i_next, j_next}) ->
        dir_next = case dir do
          :up -> :right
          :right -> :down
          :down -> :left
          :left -> :up
        end
        step({i, j, dir_next}, path, obstacles, size)
      true ->
        step({i_next, j_next, dir}, path, obstacles, size)
    end
  end
end
P1.step(pos, obstacles, size) |> Enum.count

Part 2

candidates = P1.step(pos, obstacles, size)
defmodule P2 do
  def step(pos, obstacles, size) do
    step(pos, MapSet.new(), obstacles, size)
  end

  defp step({i, j, dir} = pos, path, obstacles, {w, h} = size) do
    if MapSet.member?(path, pos) do
      true
    else
      path = MapSet.put(path, pos)

      {i_next, j_next} =
        case dir do
          :up -> {i - 1, j}
          :right -> {i, j + 1}
          :down -> {i + 1, j}
          :left -> {i, j - 1}
        end

      cond do
        i_next < 0 || i_next >= h || j_next < 0 || j_next >= w ->
          false

        MapSet.member?(obstacles, {i_next, j_next}) ->
          dir_next =
            case dir do
              :up -> :right
              :right -> :down
              :down -> :left
              :left -> :up
            end

          step({i, j, dir_next}, path, obstacles, size)

        true ->
          step({i_next, j_next, dir}, path, obstacles, size)
      end
    end
  end
end
Enum.count(candidates, fn new_obst -> P2.step(pos, MapSet.put(obstacles, new_obst), size) end)