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