Day 11: Cosmic Expansion – Advent of Code 2023
input =
File.stream!("/Users/pw/src/weiland/adventofcode/2023/input/11.txt")
|> Stream.map(&String.trim/1)
test_input = "...#......
.......#..
#.........
..........
......#...
.#........
.........#
..........
.......#..
#...#....." |> String.split("\n")
Parsing
duplicate_empty_rows = fn old_rows ->
old_rows
|> Enum.with_index()
|> Enum.reduce({old_rows, 0}, fn {row, index}, {rows, offset} ->
if Enum.all?(row, &(&1 == ".")) do
{List.insert_at(rows, index + offset, row), offset + 1}
else
{rows, offset}
end
end)
|> elem(0)
end
parse = fn input ->
rows =
input
|> Enum.map(&String.graphemes/1)
|> duplicate_empty_rows.()
# transpose cols to rows
|> Enum.zip_with(&Function.identity/1)
|> duplicate_empty_rows.()
# transpose back to rows
|> Enum.zip_with(&Function.identity/1)
|> Enum.map_reduce(0, fn row, row_count ->
Enum.map_reduce(row, row_count, fn
"#", count -> {count + 1, count + 1}
".", count -> {".", count}
end)
end)
|> elem(0)
# Enum.reduce(Enum.with_index(rows), Map.new(), fn {row, y}, map ->
# Enum.reduce(Enum.with_index(row), map, fn
# {".", _x}, acc -> acc
# {value, x}, acc -> Map.put(acc, {x, y}, value)
# end)
# end)
for(
{row, y} <- Enum.with_index(rows),
{value, x} <- Enum.with_index(row),
into: %{},
do: {{x, y}, value}
)
|> Map.reject(fn {_key, value} -> value == "." end)
end
parse.(test_input)
Part One
to_paths = fn map ->
Enum.reduce(map, [], fn {{x1, y1}, a}, lst ->
[
Enum.map(map, fn {{x2, y2}, b} ->
length = abs(x1 - x2) + abs(y1 - y2)
{a, b, length}
end)
| lst
]
end)
|> List.flatten()
|> Enum.reject(fn {_, _, v} -> v == 0 end)
end
part_one = fn input ->
input |> parse.() |> to_paths.() |> Enum.map(&elem(&1, 2)) |> Enum.sum() |> div(2)
end
part_one.(test_input) == 374 &&
part_one.(input)
Part Two
part_two = fn input ->
mat = input |> Enum.map(&String.graphemes/1)
{empty_rows, empty_cols, galaxies} =
mat
|> Enum.with_index()
|> Enum.reduce(
{0..(Enum.count(mat) - 1), 0..(Enum.count(Enum.at(mat, 0)) - 1), []},
fn {line, row}, {r, c, g} ->
line
|> Enum.with_index()
|> Enum.reduce({r, c, g}, fn {ch, col}, {r, c, g} ->
if ch == "#" do
{Enum.reject(r, &(&1 == row)), Enum.reject(c, &(&1 == col)), [{row, col} | g]}
else
{r, c, g}
end
end)
end
)
diff = 999_999
expanded =
galaxies
|> Enum.map(fn {row, col} ->
{empty_rows |> Enum.filter(&(&1 <= row)) |> Enum.count() |> then(&(&1 * diff + row)),
empty_cols |> Enum.filter(&(&1 <= col)) |> Enum.count() |> then(&(&1 * diff + col))}
end)
expanded
|> Enum.drop(-1)
|> Enum.with_index(1)
|> Enum.map(fn {a, i} ->
expanded
|> Enum.drop(i)
|> Enum.reduce(0, fn b, acc ->
acc + abs(elem(b, 0) - elem(a, 0)) + abs(elem(b, 1) - elem(a, 1))
end)
end)
|> Enum.sum()
end
part_two.(test_input) &&
part_two.(input)