You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

128 lines
3.4 KiB
Elixir

defmodule Day22 do
def run do
lines =
File.read!("data.txt")
|> String.trim()
|> String.split("\n")
|> Enum.map(&String.to_integer/1)
part1 =
Enum.reduce(lines, 0, fn secret, acc ->
acc + Enum.reduce(1..2000, secret, fn _, s -> evolve(s) end)
end)
dbg(part1)
# lines = [123, 123]
{prices, diffs} =
Enum.reduce(lines, {[], []}, fn secret, {prices_acc, diffs_acc} ->
{_, prices, diffs} =
Enum.reduce(1..2000, {secret, [price(secret)], []}, fn _, {s, prices, diffs} ->
new_secret = evolve(s)
price = price(new_secret)
diff = price - hd(prices)
{new_secret, [price | prices], [diff | diffs]}
end)
{[prices | prices_acc], [diffs | diffs_acc]}
end)
dbg({prices, diffs})
# max length of all price lists
max = Enum.map(prices, &length/1) |> Enum.max()
# sort the lists by price, keeping their original index
sorted_by_price_with_index =
Enum.map(prices, fn prices ->
prices |> Enum.with_index() |> Enum.sort(fn {p1, _}, {p2, _} -> p1 >= p2 end)
end)
|> Enum.with_index()
# dbg(sorted_by_price_with_index)
# go from 0 up to the max list length of the lists sorted by price, highest to lowest
# grab the 4-sequence of each list
# check each sequence against each list, add up the prices
# if that price is greater than the max acc price, that's the new winning sequence
part2 =
Enum.reduce(0..(max - 1), 0, fn index, acc ->
dbg({"checking all lists at index", index})
dbg({"current acc", acc})
res =
Enum.reduce(sorted_by_price_with_index, {0, []}, fn {price_list, list_index},
{max_price, max_seq} ->
{price, original_index} = Enum.at(price_list, index)
# dbg({diffs, index})
seq = Enum.at(diffs, list_index) |> Enum.slice(original_index, 4)
# dbg({price, seq})
if length(seq) == 4 do
max_price = score_seq(prices, diffs, seq)
# dbg({"max_price", max_price})
{max_price, seq}
else
{max_price, max_seq}
end
end)
if res > acc do
dbg({"new high", res})
res
else
acc
end
end)
dbg(part2)
end
defp score_seq(prices, diffs, seq) do
# dbg({"score seq", seq})
res =
Enum.zip(prices, diffs)
|> Enum.reduce(0, fn {p, d}, acc ->
case index_of_sublist(d, seq) do
nil -> acc
idx -> Enum.at(p, idx) + acc
end
end)
res
# dbg(res)
end
defp evolve(secret) do
step1 = secret |> Kernel.*(64) |> mix(secret) |> prune()
step2 = step1 |> div(32) |> mix(step1) |> prune()
step2 |> Kernel.*(2048) |> mix(step2) |> prune()
end
defp mix(num, secret) do
Bitwise.bxor(num, secret)
end
defp prune(num) do
Integer.mod(num, 16_777_216)
end
defp price(num) do
Integer.digits(num) |> Enum.take(-1) |> Integer.undigits()
end
# finds the index of a sequence in a list, nil if it isn't in there
defp index_of_sublist(parent, sublist) do
Enum.reduce_while(0..(length(parent) - length(sublist)), nil, fn idx, acc ->
if Enum.slice(parent, idx, length(sublist)) == sublist,
do: {:halt, idx},
else: {:cont, acc}
end)
end
end
Day22.run()