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, &[{{&1, 0}, :right}, {{&1, width}, :left}]),
Enum.flat_map(0..width, &[{{0, &1}, :down}, {{height, &1}, :up}])
])
start_points
|> Task.async_stream(&Day16.traverse(map, {height, width}, &1))
|> Stream.map(fn {:ok, {path, _}} -> MapSet.size(path) end)
|> Enum.max()
7572