my-solutions/advent-of-code-2023/elixir/day_09/lib/oasis.ex
2024-08-18 18:24:37 +05:00

88 lines
1.9 KiB
Elixir

defmodule Oasis do
@moduledoc """
Day 9 of Advent of Code 2023.
Link: https://adventofcode.com/2023/day/9.
"""
def solve() do
input =
File.stream!("input.txt")
|> Stream.map(&String.trim_trailing/1)
# Get lists of strings.
|> Stream.map(&String.split/1)
# Get lists of integers.
|> Stream.map(fn list -> Enum.map(list, &String.to_integer/1) end)
# Predict next value for each list.
input
|> Stream.map(fn list -> predict(list, &predict_next_value/2) end)
|> Enum.sum()
|> IO.puts()
# Predict previous value for each list.
input
|> Stream.map(fn list -> predict(list, &predict_prev_value/2) end)
|> Enum.sum()
|> IO.puts()
end
@doc """
Predict a value for the list.
Predictor_fun is a function that takes current row and previous prediction result and returns new prediction.
Examples:
iex> Oasis.predict([0, 3, 6, 9, 12, 15], &Oasis.predict_next_value/2)
18
iex> Oasis.predict([1, 3, 6, 10, 15, 21], &Oasis.predict_next_value/2)
28
iex> Oasis.predict([10, 13, 16, 21, 30, 45], &Oasis.predict_prev_value/2)
5
"""
def predict(list, predictor_fun) do
diff = get_diff(list)
if zeros_only?(diff) do
predictor_fun.(list, 0)
else
predictor_fun.(list, predict(diff, predictor_fun))
end
end
def predict_next_value(list, prev_result) do
Enum.at(list, -1) + prev_result
end
def predict_prev_value(list, prev_result) do
hd(list) - prev_result
end
@doc """
Get a list of differences between values.
Examples:
iex> Oasis.get_diff([1, 3, 6, 10, 15, 21])
[2, 3, 4, 5, 6]
"""
def get_diff([head | tail]) do
case tail do
[] -> []
[val | _] -> [val - head | get_diff(tail)]
end
end
def zeros_only?(list) do
case list do
[] ->
true
[head | tail] ->
if head != 0, do: false, else: zeros_only?(tail)
end
end
end