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

Day 16

2023/day16.livemd

Day 16

Mix.install([:kino_aoc])

Section

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "16", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
{:ok,
 "\\\\.......\\.............../-........|......................-......../..................../......./....|........\n\\.................\\....\\...../........|........|....../.\\....-....-..................|../...|...\\./...........\n../...-.....................\\./-.....-.........................../...........\\|......\\......--............/...\n.......|........\\...\\.........../|...../.........../...../..../...../.................\\.././..................\n...........\\|..........-.................|.|.............|.............|....|..|-...........-..-......./......\n.............................-../.......\\.........\\..........................\\.......|....|.........-.........\n..................../......................-......-...................................../...\\...........|.....\n..|..............................\\......................|....-................................................\n..........\\........./.........................|.................../....-|...../............./.....|.........//\n.-./../.................|....................|................../................\\....|..|..../|.......-./....\n................/........................\\...|.....-.....-...-........\\.............../.......-|-./...........\n.......\\|.........|..-.........................|../...../.\\.../............../.........|.....|../........\\....\n..............-./.............../...........................|....-....-..../......\\....-.......\\........./....\n...............././.-.......|/...................../..-..-..\\...\\.-.-..-................../...................\n..-......../.-.......\\..............................\\/....................................-...................\n.........................................\\....\\.........\\.-......-.|.....\\.......................\\............\n./.........\\.-.-/.............................................|/......|........-\\-............................\n-.....................|.........................|..\\..........|..--....\\..\\........./.........................\n...............\\............../............\\.........................-...............|................/....\\..\n.-.........-...........-|......../...............|.........-...-...|....................-.....................\n.\\./...............|......\\..........-..\\.................-...........................././../.-............/.-\n...-/..............-..|........................|..../.......-..........-/...............\\............../......\n|..........-\\..........\\......../.................\\.|.......-............../.......|.................../......\n.....\\...............|................/........\\.....\\..................-.../........................./.......\n..............................-..\\.../.........-..-........-.......-.............-......................|.../.\n.........../../.|.......-/-.|.|...-...............................\\\\./....|................/.\\................\n.........-..............................|.||......................./........\\..-..../.................|...../.\n.............\\...../\\.............../...-......../.....\\.....................................-/........\\..|..\\\n...............-.-../.-./....|.......\\................/...|................................................|.|\n........-.................-.........................-................\\......|.....\\......-........-...........\n|../.......|......\\............|....../..|.......|../.........................................................\n....................................../..\\....\\\\.............\\...................||/.../.../........\\.........\n............./..../....-...|........../-.............\\......................................\\/........-..-....\n.................\\\\...............|\\\\...........\\.........../......-.......-............\\..............-..-..\\\n...|..../..|../..|........./.../.-..............................\\.........-........................|.\\....||..\n......................./..|....-..............................-........................././../................\n../.\\.............//.\\.|..................-.....\\...........|..././............/...................." <> ...}
# puzzle_input =
~S"""
.|...\....
|.-.\.....
.....|-...
........|.
..........
.........\
..../.\\..
.-.-/..|..
.|....-|.\
..//.|....
"""
".|...\\....\n|.-.\\.....\n.....|-...\n........|.\n..........\n.........\\\n..../.\\\\..\n.-.-/..|..\n.|....-|.\\\n..//.|....\n"
lines = String.split(puzzle_input, "\n", trim: true)

map =
  for {line, y} <- Enum.with_index(lines),
      {char, x} <- line |> String.to_charlist() |> Enum.with_index(),
      char not in ~c".",
      into: %{},
      do: {{y, x}, <>}

{height, width} = {byte_size(hd(lines)) - 1, length(lines) - 1}
{109, 109}
defmodule Day16 do
  defguard in_map(yx, hw) when elem(yx, 0) in 0..elem(hw, 0) and elem(yx, 1) in 0..elem(hw, 1)

  def traverse(map, wh, start \\ {{0, 0}, :right}),
    do: do_traverse(map, wh, start, _acc = MapSet.new(), _visited = MapSet.new())

  defp do_traverse(map, hw, {xy, dir} = pos, acc, visited) when in_map(xy, hw) do
    if pos not in visited do
      acc = MapSet.put(acc, xy)

      case Map.fetch(map, xy) do
        :error ->
          do_traverse(map, hw, next(xy, dir), acc, visited)

        {:ok, "|"} when dir in ~w[up down]a ->
          do_traverse(map, hw, next(xy, dir), acc, visited)

        {:ok, "-"} when dir in ~w[left right]a ->
          do_traverse(map, hw, next(xy, dir), acc, visited)

        {:ok, mirror} when mirror in ~w[/ \\] ->
          next = mirror(mirror, dir)
          do_traverse(map, hw, next(xy, next), acc, MapSet.put(visited, pos))

        {:ok, "|"} when dir in ~w[left right]a ->
          # Add both sides to visited, as it will result in the same outcome
          visited =
            visited
            |> MapSet.put({xy, :left})
            |> MapSet.put({xy, :right})

          {acc, visited} = do_traverse(map, hw, next(xy, :up), acc, visited)
          do_traverse(map, hw, next(xy, :down), acc, visited)

        {:ok, "-"} when dir in ~w[up down]a ->
          # Add both sides to visited, as it will result in the same outcome
          visited =
            visited
            |> MapSet.put({xy, :up})
            |> MapSet.put({xy, :down})

          {acc, visited} = do_traverse(map, hw, next(xy, :left), acc, visited)
          do_traverse(map, hw, next(xy, :right), acc, visited)
      end
    else
      {acc, visited}
    end
  end

  defp do_traverse(_, _wh, _pos, acc, visited) do
    {acc, visited}
  end

  defp mirror("/", :up), do: :right
  defp mirror("/", :left), do: :down
  defp mirror("/", :down), do: :left
  defp mirror("/", :right), do: :up

  defp mirror("\\", :up), do: :left
  defp mirror("\\", :left), do: :up
  defp mirror("\\", :down), do: :right
  defp mirror("\\", :right), do: :down

  defp next({y, x}, :up), do: {{y - 1, x}, :up}
  defp next({y, x}, :left), do: {{y, x - 1}, :left}
  defp next({y, x}, :down), do: {{y + 1, x}, :down}
  defp next({y, x}, :right), do: {{y, x + 1}, :right}
end
{:module, Day16, <<70, 79, 82, 49, 0, 0, 28, ...>>, {:next, 2}}

Part 1

{path, _} = Day16.traverse(map, {height, width})

MapSet.size(path)
7242

Part 2

start_points =
  Enum.concat([
    Enum.flat_map(0..height, &amp;[{{&amp;1, 0}, :right}, {{&amp;1, width}, :left}]),
    Enum.flat_map(0..width, &amp;[{{0, &amp;1}, :down}, {{height, &amp;1}, :up}])
  ])

start_points
|> Task.async_stream(&amp;Day16.traverse(map, {height, width}, &amp;1))
|> Stream.map(fn {:ok, {path, _}} -> MapSet.size(path) end)
|> Enum.max()
7572