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

Day 10 Part 1

2023/10_part1.livemd

Day 10 Part 1

Mix.install([
  {:kino, "~> 0.11.3"}
])

Input

example1 = """
.....
.S-7.
.|.|.
.L-J.
.....
"""

example1_complex = """
-L|F7
7S-7|
L|7||
-L-J|
L|-JF
"""

example2 = """
..F7.
.FJ|.
SJ.L7
|F--J
LJ...
"""

example2_complex = """
7-F7-
.FJ|7
SJLL7
|F--J
LJ.LJ
"""

input = Kino.Input.textarea("Input", default: example1)

Part 1

expected = [4, 8]
[4, 8]
defmodule Part1 do
  def parse(input) do
    for {line, row} <- String.split(input, "\n", trim: true) |> Enum.with_index(), reduce: %{} do
      acc ->
        for {char, col} <- String.codepoints(line) |> Enum.with_index(), into: acc do
          {{row, col}, char}
        end
    end
  end

  def next(tiles, {row, col} = current) do
    coords =
      case tiles[current] do
        "|" -> [{row - 1, col}, {row + 1, col}]
        "-" -> [{row, col - 1}, {row, col + 1}]
        "L" -> [{row - 1, col}, {row, col + 1}]
        "J" -> [{row - 1, col}, {row, col - 1}]
        "7" -> [{row, col - 1}, {row + 1, col}]
        "F" -> [{row, col + 1}, {row + 1, col}]
        _ -> []
      end

    List.flatten(coords)
    |> Enum.filter(&amp;Map.has_key?(tiles, &amp;1))
  end

  def loop(_tiles, [], count, _visited), do: count

  def loop(tiles, current, count, visited) do
    to_visit = Enum.flat_map(current, &amp;next(tiles, &amp;1)) |> Enum.filter(&amp;(&amp;1 not in visited))
    loop(tiles, to_visit, count + 1, MapSet.union(visited, MapSet.new(current)))
  end

  def run(input) do
    tiles = parse(input)
    {start_row, start_col} = start = Enum.find_value(tiles, fn {k, v} -> v == "S" &amp;&amp; k end)

    to_visit =
      for x <- -1..1,
          y <- -1..1,
          candidate = {start_row + x, start_col + y},
          candidate != start,
          start in next(tiles, candidate),
          do: candidate

    loop(tiles, to_visit, 0, MapSet.new())
  end
end

[Part1.run(example1), Part1.run(example2)] == expected and
  Part1.run(example1) == Part1.run(example1_complex) and
  Part1.run(example2) == Part1.run(example2_complex)
true
Kino.Input.read(input)
|> Part1.run()
|> then(&amp;Kino.Markdown.new("Part 1: #{&amp;1}"))