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()