Day 14: Parabolic Reflector Dish – Advent of Code 2023
input =
File.stream!("/Users/pw/src/weiland/adventofcode/2023/input/14.txt")
|> Stream.map(&String.trim/1)
test_input =
"O....#....
O.OO#....#
.....##...
OO.#O....O
.O.....O#.
O.#..O.#.#
..O..#O..O
.......O..
#....###..
#OO..#...." |> String.split("\n")
Parsing
parse = fn input ->
input
|> Enum.map(&String.graphemes(&1))
end
parse.(test_input)
Part One
shift = fn lines ->
lines
|> Enum.map(fn line ->
line
|> Enum.chunk_by(&(&1 == "#"))
|> Enum.flat_map(&Enum.sort(&1, :desc))
end)
end
count = fn lines ->
lines
|> Enum.map(fn line ->
line
|> Enum.reverse()
|> Enum.with_index(1)
|> Enum.reverse()
|> Enum.filter(fn {curr, _i} -> curr == "O" end)
|> Enum.map(&elem(&1, 1))
|> Enum.reduce(0, &+/2)
end)
end
part_one = fn input ->
input
|> parse.()
# default -> north
# transpose
|> Enum.zip_with(&Function.identity/1)
# moves 0 to front
|> shift.()
# count
|> count.()
|> Enum.sum()
end
part_one.(test_input) == 136 &&
part_one.(input)
Part Two
cycle = fn lines ->
lines
# west (start) -> transpose to north
|> Enum.zip_with(&Function.identity/1)
# moves 0 to front
|> shift.()
# transpose back to west
|> Enum.zip_with(&Function.identity/1)
|> shift.()
# transpose to north
|> Enum.zip_with(&Function.identity/1)
# to south
|> Enum.map(&Enum.reverse/1)
|> shift.()
# back to north
|> Enum.map(&Enum.reverse/1)
# transpose to west (begin)
|> Enum.zip_with(&Function.identity/1)
# to east
|> Enum.map(&Enum.reverse/1)
|> shift.()
# back to west (begin)
|> Enum.map(&Enum.reverse/1)
end
n_times = 1_000_000_000
part_two = fn input ->
lines =
input
|> parse.()
configs =
1..n_times
|> Enum.reduce_while([lines, Map.new()], fn index, [lns, map] ->
res = lns |> cycle.()
{_, has} = Map.get(map, res, {nil, nil})
cond do
has == nil -> {:cont, [res, Map.put(map, res, {index, 1})]}
has == 1 -> {:cont, [res, Map.put(map, res, {index, 2})]}
true -> {:halt, map}
end
end)
repeats =
configs
|> Enum.filter(fn {_k, {_i, v}} -> v == 2 end)
|> Enum.sort(fn {_k, {a, _v}}, {_k2, {b, _v2}} -> a < b end)
|> Enum.map(fn {k, _count} -> k end)
offset = Enum.count(configs) - Enum.count(repeats)
index = rem(n_times - offset, Enum.count(repeats)) - 1
Enum.at(repeats, index) |> Enum.zip_with(&Function.identity/1) |> count.() |> Enum.sum()
end
# &&
part_two.(test_input) == 64
part_two.(input)