Advent of Code 2023 Day 16
Mix.install([
{:kino_aoc, "~> 0.1.5"}
])
Prep
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2023", "16", System.fetch_env!("LB_AOC_SESSION"))
grid =
for {row, i} <- puzzle_input |> String.split() |> Stream.with_index(),
{val, j} <- row |> String.to_charlist() |> Stream.with_index(),
into: %{},
do: {{i, j}, val}
defmodule AoC2023.Day16 do
def part1(grid) do
shoot(grid, {0, 0}, {0, 1}, MapSet.new())
|> count_energized()
end
def part2(grid) do
{max_i, max_j} = grid |> Map.keys() |> Enum.max()
count =
0..max_i
|> Enum.map(&{&1, 0})
|> Task.async_stream(&count_energized(shoot(grid, &1, {0, 1}, MapSet.new())),
ordered: false
)
|> Enum.max()
count =
0..max_i
|> Enum.map(&{&1, max_j})
|> Task.async_stream(&count_energized(shoot(grid, &1, {0, -1}, MapSet.new())),
ordered: false
)
|> Enum.reduce(count, &max/2)
count =
0..max_j
|> Enum.map(&{0, &1})
|> Task.async_stream(&count_energized(shoot(grid, &1, {1, 0}, MapSet.new())),
ordered: false
)
|> Enum.reduce(count, &max/2)
count =
0..max_j
|> Enum.map(&{max_i, &1})
|> Task.async_stream(&count_energized(shoot(grid, &1, {-1, 0}, MapSet.new())),
ordered: false
)
|> Enum.reduce(count, &max/2)
elem(count, 1)
end
defp count_energized(set) do
set
|> Enum.map(&elem(&1, 0))
|> Enum.uniq()
|> length()
end
defp shoot(grid, {i, j} = position, {di, dj} = direction, acc) do
if {position, direction} in acc do
acc
else
case grid[position] do
nil ->
acc
?. ->
shoot(grid, {i + di, j + dj}, direction, MapSet.put(acc, {position, direction}))
?\\ ->
shoot(grid, {i + dj, j + di}, {dj, di}, MapSet.put(acc, {position, direction}))
?/ ->
shoot(grid, {i - dj, j - di}, {-dj, -di}, MapSet.put(acc, {position, direction}))
?- when di == 0 ->
shoot(grid, {i + di, j + dj}, direction, MapSet.put(acc, {position, direction}))
?| when dj == 0 ->
shoot(grid, {i + di, j + dj}, direction, MapSet.put(acc, {position, direction}))
?- ->
acc = shoot(grid, {i, j - 1}, {0, -1}, MapSet.put(acc, {position, direction}))
shoot(grid, {i, j + 1}, {0, 1}, MapSet.put(acc, {position, direction}))
?| ->
acc = shoot(grid, {i - 1, j}, {-1, 0}, MapSet.put(acc, {position, direction}))
shoot(grid, {i + 1, j}, {1, 0}, MapSet.put(acc, {position, direction}))
end
end
end
end
AoC2023.Day16.part1(grid)
AoC2023.Day16.part2(grid)