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

AoC 2023 Day 10

2023/day10.livemd

AoC 2023 Day 10

Mix.install(
  [
    {:kino_aoc, "~> 0.1.5"},
    {:libgraph, "~> 0.16.0"},
    {:kino, "~> 0.11.3"}
  ],
  consolidate_protocols: false
)

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "10", System.fetch_env!("LB_AOC_COOKIE_SECRET"))
# puzzle_input = """
# 7-F7-
# .FJ|7
# SJLL7
# |F--J
# LJ.LJ
# """ |> String.trim()
defimpl Kino.Render, for: Graph do
  def to_livebook(graph) do
    graph
    |> to_mermaid()
    |> Kino.Mermaid.new()
    |> Kino.Render.to_livebook()
  end

  def to_mermaid(graph) do
    edge =
      case graph.type do
        :directed -> "-->"
        :undirected -> "---"
      end

    vertices =
      graph.vertices
      |> Enum.map(fn {v, l} -> "#{v}[\"#{l}\"]" end)
      |> Enum.join("\n")

    edges =
      graph.edges
      |> Enum.map(fn {{from, to}, _} -> "#{from} #{edge} #{to}" end)
      |> Enum.join("\n")

    """
    flowchart TD
    #{vertices}
    #{edges}
    """
  end
end

defimpl String.Chars, for: Tuple do
  def to_string({y, x}), do: "(#{y}, #{x})"
end

Parsing

defmodule Pipes do
  @offsets %{
    north: {-1, 0},
    east: {0, 1},
    west: {0, -1},
    south: {1, 0}
  }

  def parse_input(input) do
    points = indexed_points(input)

    edges =
      Enum.flat_map(points, fn {point, char} ->
        directions = connections(char)

        for direction <- directions do
          to = step(point, direction)
          {point, to}
        end
      end)

    # This should really be an undirected graph
    # But those are somewhat broken in libgraph
    graph =
      Graph.new(type: :directed)
      |> Graph.add_edges(List.flatten(edges))

    {start, _} = Enum.find(points, &amp;match?({_, "S"}, &amp;1))

    Graph.replace_vertex(graph, start, :start)
  end

  def connections("|"), do: [:north, :south]
  def connections("-"), do: [:east, :west]
  def connections("L"), do: [:north, :east]
  def connections("J"), do: [:north, :west]
  def connections("7"), do: [:south, :west]
  def connections("F"), do: [:south, :east]
  def connections("."), do: []
  def connections("S"), do: [:north, :east, :west, :south]

  def step({y, x}, dir) do
    {dy, dx} = @offsets[dir]
    {y + dy, x + dx}
  end

  def indexed_points(input) do
    lines =
      String.split(input, "\n")
      |> Enum.with_index()

    for {line, y} <- lines do
      columns =
        String.codepoints(line)
        |> Enum.with_index()

      for {character, x} <- columns do
        {{y, x}, character}
      end
    end
    |> List.flatten()
  end
end
graph = Pipes.parse_input(puzzle_input)

Part 1

path = Graph.get_paths(graph, :start, :start) |> Enum.max()

Graph.subgraph(graph, path)
div(length(path) - 1, 2)

Part 2