Advent of Code - Day 9
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Introduction
–> Content
Puzzle
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2023", "9", System.fetch_env!("LB_AOC_SESSION"))
Parser
Code - Parser
defmodule Parser do
def parse(input) do
String.split(input, "\n", trim: true)
|> Enum.map(fn string ->
String.split(string, " ", trim: true)
|> Enum.map(&String.to_integer(&1))
end)
end
end
Tests - Parser
ExUnit.start(autorun: false)
defmodule ParserTest do
use ExUnit.Case, async: true
import Parser
@input1 """
0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45
"""
@expected1 [[0, 3, 6, 9, 12, 15], [1, 3, 6, 10, 15, 21], [10, 13, 16, 21, 30, 45]]
describe "parse test" do
test "input 1" do
actual = parse(@input1)
assert actual == @expected1
end
end
end
ExUnit.run()
Part One
Code - Part 1
defmodule PartOne do
def solve(input) do
IO.puts("--- Part One ---")
IO.puts("Result: #{run(input)}")
end
def run(input_string) do
histories = Parser.parse(input_string)
Enum.map(histories, fn history ->
report = report_for_history(history)
report_revised = report_with_extrapolation(report)
extrapolated_value_in_report(report_revised)
end)
|> Enum.sum()
end
def report_for_history(history) do
deltas = Enum.zip_with([history, tl(history)], fn [first, second] -> second - first end)
if Enum.all?(deltas, &(&1 == 0)) do
[history] ++ [deltas]
else
[history] ++ report_for_history(deltas)
end
end
def report_with_extrapolation(report) do
# IO.puts("")
# IO.puts("")
report = List.update_at(report, Enum.count(report) - 1, fn zeros -> zeros ++ [0] end)
{new_report, _} =
Enum.reduce(
report |> Enum.reverse() |> tl(),
{[List.last(report)], 0},
fn sequence, {new_report, value_below} ->
# IO.puts("")
# IO.inspect(new_report, label: "new_report")
# IO.inspect(value_below, label: "value_below")
value_left = List.last(sequence)
extrapolation = value_left + value_below
# IO.inspect(value_left, label: "value_left")
# IO.inspect(extrapolation, label: "extrapolation")
{[sequence ++ [extrapolation]] ++ new_report, extrapolation}
end
)
new_report
end
def extrapolated_value_in_report(report) do
report |> hd() |> List.last()
end
end
Tests - Part 1
ExUnit.start(autorun: false)
defmodule PartOneTest do
use ExUnit.Case, async: true
import PartOne
@input1 """
0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45
"""
@expected1 114
test "simple example 1" do
actual = run(@input1)
assert actual == @expected1
end
describe "report_for_history/1" do
test "example 1" do
actual = report_for_history([0, 3, 6, 9, 12, 15])
assert actual == [[0, 3, 6, 9, 12, 15], [3, 3, 3, 3, 3], [0, 0, 0, 0]]
end
test "example 2" do
actual = report_for_history([1, 3, 6, 10, 15, 21])
assert actual == [[1, 3, 6, 10, 15, 21], [2, 3, 4, 5, 6], [1, 1, 1, 1], [0, 0, 0]]
end
test "example 3" do
actual = report_for_history([10, 13, 16, 21, 30, 45])
assert actual == [
[10, 13, 16, 21, 30, 45],
[3, 3, 5, 9, 15],
[0, 2, 4, 6],
[2, 2, 2],
[0, 0]
]
end
end
describe "report_with_extrapolation/1" do
test "example 1" do
actual = report_with_extrapolation([[0, 3, 6, 9, 12, 15], [3, 3, 3, 3, 3], [0, 0, 0, 0]])
assert actual == [[0, 3, 6, 9, 12, 15, 18], [3, 3, 3, 3, 3, 3], [0, 0, 0, 0, 0]]
end
test "example 2" do
actual =
report_with_extrapolation([
[1, 3, 6, 10, 15, 21],
[2, 3, 4, 5, 6],
[1, 1, 1, 1],
[0, 0, 0]
])
assert actual == [
[1, 3, 6, 10, 15, 21, 28],
[2, 3, 4, 5, 6, 7],
[1, 1, 1, 1, 1],
[0, 0, 0, 0]
]
end
test "example 3" do
actual =
report_with_extrapolation([
[10, 13, 16, 21, 30, 45],
[3, 3, 5, 9, 15],
[0, 2, 4, 6],
[2, 2, 2],
[0, 0]
])
assert actual == [
[10, 13, 16, 21, 30, 45, 68],
[3, 3, 5, 9, 15, 23],
[0, 2, 4, 6, 8],
[2, 2, 2, 2],
[0, 0, 0]
]
end
end
end
ExUnit.run()
Solution - Part 1
PartOne.solve(puzzle_input)
Part Two
Code - Part 2
defmodule PartTwo do
def solve(input) do
IO.puts("--- Part Two ---")
IO.puts("Result: #{run(input)}")
end
def run(input_string) do
histories = Parser.parse(input_string)
Enum.map(histories, fn history ->
report = PartOne.report_for_history(history)
report_revised = report_with_extrapolation(report)
extrapolated_value_in_report(report_revised)
end)
|> Enum.sum()
end
def report_with_extrapolation(report) do
# IO.puts("")
# IO.puts("")
report = List.update_at(report, Enum.count(report) - 1, fn zeros -> [0] ++ zeros end)
{new_report, _} =
Enum.reduce(
report |> Enum.reverse() |> tl(),
{[List.last(report)], 0},
fn sequence, {new_report, value_below} ->
# IO.puts("")
# IO.inspect(new_report, label: "new_report")
# IO.inspect(value_below, label: "value_below")
value_right = hd(sequence)
extrapolation = value_right - value_below
# IO.inspect(value_left, label: "value_left")
# IO.inspect(extrapolation, label: "extrapolation")
{[[extrapolation] ++ sequence] ++ new_report, extrapolation}
end
)
new_report
end
def extrapolated_value_in_report(report) do
report |> hd() |> hd()
end
end
Tests - Part 2
ExUnit.start(autorun: false)
defmodule PartTwoTest do
use ExUnit.Case, async: true
import PartTwo
@input1 """
0 3 6 9 12 15
1 3 6 10 15 21
10 13 16 21 30 45
"""
@expected1 2
test "simple example 1" do
actual = run(@input1)
assert actual == @expected1
end
describe "report_with_extrapolation/1" do
test "example 3" do
actual =
report_with_extrapolation([
[10, 13, 16, 21, 30, 45],
[3, 3, 5, 9, 15],
[0, 2, 4, 6],
[2, 2, 2],
[0, 0]
])
assert actual == [
[5, 10, 13, 16, 21, 30, 45],
[5, 3, 3, 5, 9, 15],
[-2, 0, 2, 4, 6],
[2, 2, 2, 2],
[0, 0, 0]
]
end
end
end
ExUnit.run()
Solution - Part 2
PartTwo.solve(puzzle_input)