defmodule Aoc2023.Day10 do use Aoc2023 require Integer @connections %{ ?| => [{-1, 0}, {1, 0}], ?- => [{0, -1}, {0, 1}], ?L => [{-1, 0}, {0, 1}], ?J => [{-1, 0}, {0, -1}], ?7 => [{1, 0}, {0, -1}], ?F => [{1, 0}, {0, 1}], ?. => [], ?S => [] } defp add_coords({y, x}, {y_, x_}), do: {y + y_, x + x_} defp path(s_conns, conns) do loop = fn recur, checked, checking -> checked_ = MapSet.union(checked, checking) checking_ = checking |> Enum.flat_map(&Map.get(conns, &1, [])) |> MapSet.new() |> MapSet.difference(checked_) if MapSet.size(checking_) > 0, do: recur.(recur, checked_, checking_), else: checked_ end loop.(loop, MapSet.new(), MapSet.new(s_conns)) end def parse(input) do grid = input |> lines |> Enum.map(&String.to_charlist/1) |> Enum.with_index() s = grid |> Enum.find_value(fn {line, y} -> x = line |> Enum.find_index(&(&1 == ?S)) if x, do: {y, x} end) conns = grid |> Enum.flat_map(fn {row, y} -> row |> Enum.with_index() |> Enum.map(fn {c, x} -> { {y, x}, Map.get(@connections, c) |> Enum.map(pipe(add_coords({y, x}))) } end) end) |> Enum.into(%{}) s_conns = conns |> Enum.filter(&(s in elem(&1, 1))) |> Enum.map(pipe(elem(0))) {s, s_conns, grid, path(s_conns, conns)} end def part1({_, _, _, path}) do path |> MapSet.size() |> then(&(&1 / 2)) |> trunc() end def part2({{sy, sx}, s_conns, grid, path}) do s_dirs = s_conns |> Enum.map(pipe(add_coords({-sy, -sx}))) |> Enum.sort() s_char = @connections |> Enum.find(fn {_, conns} -> Enum.sort(conns) == s_dirs end) |> elem(0) grid_ = grid |> list_map_at(sy, pipe(map_nth(0, pipe(List.replace_at(sx, s_char))))) grid_ |> Stream.map(fn {line, y} -> for x <- 0..length(line) - 1, reduce: {false, 0} do {within, area} -> cond do {y, x} in path and Enum.at(line, x) in [?L, ?J, ?|] -> {not within, area} {y, x} in path -> {within, area} within -> {within, area + 1} true -> {within, area} end end |> elem(1) end) |> Enum.sum() end end