Day 9
Mix.install([
{:req, "~> 0.4.5"},
{:kino, "~> 0.11.3"},
{:vega_lite, "~> 0.1.8"},
{:kino_vega_lite, "~> 0.1.11"}
])
Input
input =
Req.get!(
"https://adventofcode.com/2023/day/9/input",
headers: [{"Cookie", ~s"session=#{System.fetch_env!("LB_AOC_SESSION")}"}]
).body
Kino.Text.new(input, terminal: true)
Part 1
histories =
for line <- String.split(input, "\n", trim: true) do
String.split(line)
|> Enum.map(&String.to_integer/1)
end
defmodule Part1 do
def difference(history, fun), do: difference(history, [], fun)
def difference(prev_diffs, rest_diffs, fun) do
if Enum.all?(prev_diffs, &(&1 == 0)) do
fun.(rest_diffs, 0)
else
next_diffs =
prev_diffs
|> Enum.chunk_every(2, 1, :discard)
|> Enum.map(fn xs -> Enum.reduce(xs, &Kernel.-/2) end)
difference(next_diffs, [prev_diffs | rest_diffs], fun)
end
end
def predictr([], extrapolation), do: extrapolation
def predictr([diffs | rest], extrapolation) do
predictr(rest, extrapolation + List.last(diffs))
end
end
histories
|> Enum.map(fn history -> Part1.difference(history, &Part1.predictr/2) end)
|> Enum.sum()
Part 2
defmodule Part2 do
def predictl([], extrapolation), do: extrapolation
def predictl([[first | _] | rest], extrapolation) do
predictl(rest, first - extrapolation)
end
end
histories
|> Enum.map(fn history -> Part1.difference(history, &Part2.predictl/2) end)
|> Enum.sum()
Viz
alias VegaLite, as: Vl
defmodule Viz do
def difference(widget, history), do: difference(widget, history, [])
def difference(widget, prev_diffs, rest_diffs) do
i = length(rest_diffs)
data =
for {diff, x} <- Enum.with_index(prev_diffs) do
%{"x" => x, "y" => diff, "i" => i}
end
Kino.VegaLite.push_many(widget, data)
Process.sleep(div(500, i + 1))
if Enum.all?(prev_diffs, &(&1 == 0)) do
predictr(widget, [prev_diffs | rest_diffs])
else
next_diffs =
prev_diffs
|> Enum.chunk_every(2, 1, :discard)
|> Enum.map(fn xs -> Enum.reduce(xs, &Kernel.-/2) end)
difference(widget, next_diffs, [prev_diffs | rest_diffs])
end
end
def predictr(widget, diffs), do: predictr(widget, diffs, 0)
def predictr(_, [], extrapolation), do: extrapolation
def predictr(widget, [diffs | rest], extrapolation) do
next_extrapolation = extrapolation + List.last(diffs)
i = length(rest)
Kino.VegaLite.push(widget, %{
"x" => length(diffs),
"y" => next_extrapolation,
"i" => i
})
Process.sleep(div(500, i + 1))
predictr(widget, rest, next_extrapolation)
end
end
widget =
Vl.new(width: 600, height: 300)
|> Vl.mark(:line)
|> Vl.encode_field(:x, "x", type: :quantitative)
|> Vl.encode_field(:y, "y", type: :quantitative)
|> Vl.encode_field(:color, "i", type: :nominal)
|> Kino.VegaLite.render()
for history <- histories do
Kino.VegaLite.clear(widget)
Viz.difference(widget, history)
end