Powered by AppSignal & Oban Pro

🎄 Year 2023 🔔 Day 10

elixir/notebooks/2023/day10.livemd

🎄 Year 2023 🔔 Day 10

Setup

map =
  File.read!("#{__DIR__}/../../../inputs/2023/day10.txt")
  |> String.split("\n", trim: true)
  |> Enum.map(fn line ->
    line
    |> String.split("", trim: true)
  end)
  |> Enum.with_index(fn line, y ->
    Enum.with_index(line, fn pipe, x -> {{x, y}, pipe} end)
  end)
  |> Enum.concat()
  |> Map.new()

Part 1

defmodule Helper do
  def get_next_location(map, prev_coords, {cur_x, cur_y}, pipe) do
    {coords1, coords2} =
      case pipe do
        "|" -> {{cur_x, cur_y - 1}, {cur_x, cur_y + 1}}
        "-" -> {{cur_x - 1, cur_y}, {cur_x + 1, cur_y}}
        "L" -> {{cur_x, cur_y - 1}, {cur_x + 1, cur_y}}
        "J" -> {{cur_x, cur_y - 1}, {cur_x - 1, cur_y}}
        "7" -> {{cur_x - 1, cur_y}, {cur_x, cur_y + 1}}
        "F" -> {{cur_x + 1, cur_y}, {cur_x, cur_y + 1}}
      end

    if coords1 != prev_coords do
      {coords1, Map.fetch!(map, coords1)}
    else
      {coords2, Map.fetch!(map, coords2)}
    end
  end
end

[{start_coords, "S"}] = Enum.filter(map, fn {{_x, _y}, pipe} -> pipe == "S" end)

map
|> Map.update!(start_coords, fn _ -> "J" end)

locations =
  Stream.unfold({{nil, nil}, {start_coords, "J"}}, fn acc ->
    {{prev_coords, _prev_pipe}, {cur_coords, cur_pipe}} = acc

    if cur_coords == start_coords && prev_coords != nil do
      nil
    else
      next_location = Helper.get_next_location(map, prev_coords, cur_coords, cur_pipe)
      cur_location = {cur_coords, cur_pipe}
      {cur_location, {cur_location, next_location}}
    end
  end)

locations
|> Enum.count()
|> then(&div(&1, 2))

Part 2

require Integer

locations_map = Map.new(locations)

map =
  map
  |> Enum.map(fn {coords, pipe} ->
    pipe = if pipe == "S", do: "J", else: pipe
    if Map.has_key?(locations_map, coords), do: {coords, pipe}, else: {coords, "."}
  end)

map
|> Enum.group_by(fn {{_x, y}, _pipe} -> y end)
|> Enum.map(fn {_y, coords} ->
  coords
  |> Enum.sort_by(fn {{x, y}, _pipe} -> {y, x} end, :asc)
  |> Enum.map(&elem(&1, 1))
end)
|> Enum.map(fn chars ->
  Enum.reduce(
    chars,
    {_prev_bend = nil, _vertical_count = 0, _inner_count = 0},
    fn char, {prev_bend, vertical_count, inner_count} ->
      case {prev_bend, char} do
        {nil, "."} when Integer.is_odd(vertical_count) ->
          {prev_bend, vertical_count, inner_count + 1}

        {nil, "."} ->
          {prev_bend, vertical_count, inner_count}

        {nil, "|"} ->
          {prev_bend, vertical_count + 1, inner_count}

        {nil, "F"} ->
          {"F", vertical_count, inner_count}

        {nil, "L"} ->
          {"L", vertical_count, inner_count}

        {"F", "J"} ->
          {nil, vertical_count + 1, inner_count}

        {"F", "7"} ->
          {nil, vertical_count, inner_count}

        {"L", "J"} ->
          {nil, vertical_count, inner_count}

        {"L", "7"} ->
          {nil, vertical_count + 1, inner_count}

        {last_bend, "-"} ->
          {last_bend, vertical_count, inner_count}
      end
    end
  )
  |> elem(2)
end)
|> Enum.sum()