Not sure why I could not think clearly with this one. Did quite the scribble with paper and pencil until I took the more “procedural looking” path.
Took 20 seconds to complete when using List (Like I said, procedural path) so I used Aja.Vector and replaced Enum and List and got that under a second, part 2 takes ~6 seconds though.
I am curious to see how others have done it, especially the performance.
Here’s the code:
defmodule AdventOfCode.Y2022.Day20 do
alias AdventOfCode.Helpers.InputReader
alias Aja.Vector
def input, do: InputReader.read_from_file(2022, 20)
def run(input \\ input()) do
input = parse(input)
run_1 = Task.async(fn -> run_1(input) end)
run_2 = Task.async(fn -> run_2(input) end)
{Task.await(run_1, :infinity), Task.await(run_2, :infinity)}
end
defp run_1(input) do
compute_grove_sum(input, 1)
end
@decryption_key 811_589_153
def run_2(input) do
input
|> Vector.map(fn {v, i} -> {v * @decryption_key, i} end)
|> compute_grove_sum(10)
end
def parse(data \\ input()) do
data
|> String.split("\n", trim: true)
|> Enum.map(&String.to_integer/1)
|> Enum.with_index()
|> Vector.new()
end
defp compute_grove_sum(input, repeat) do
n = Vector.size(input) - 1
sequence = mix(input, n, repeat)
zero_idx = index_of(sequence, n, 0)
grove_sum(sequence, n, zero_idx)
end
defp grove_sum(sequence, n, zero_idx) do
[1000, 2000, 3000]
|> Enum.reduce(0, fn x, acc ->
{val, _} = sequence[rem(zero_idx + x, n + 1)]
acc + val
end)
end
defp mix(input, n, repeat) do
1..repeat
|> Enum.reduce(input, fn _, repeated_acc ->
0..n
|> Enum.reduce(repeated_acc, fn i, acc1 ->
j =
Enum.reduce_while(0..n, acc1, fn j, acc2 ->
{_, idx} = acc1[j]
case idx do
^i -> {:halt, j}
_ -> {:cont, acc2}
end
end)
{val, _} = acc1[j]
{_, popped} = Vector.pop_at(acc1, j)
ins = (j + val) |> Integer.mod(n)
insert_at(popped, ins, {val, i})
end)
end)
end
defp insert_at(vector, idx, value) do
{left, right} = Vector.split(vector, idx)
left |> Vector.concat([value]) |> Vector.concat(right)
end
defp index_of(sequence, n, idx) do
Enum.reduce_while(0..(n - 1), nil, fn x, acc ->
case sequence[x] do
{^idx, _} ->
{:halt, x}
_ ->
{:cont, acc}
end
end)
end
end