Day 1
Mix.install([
{:kino_aoc, "~> 0.1"}
])
Input
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2024", "1", System.fetch_env!("LB_SESSION"))
test input
test_input = """
3 4
4 3
2 5
1 3
3 9
3 3
"""
parser
defmodule RowParser do
def parse(input) do
input
|> String.split("\n", trim: true)
|> Stream.map(&String.trim_leading/1)
|> Stream.map(fn str ->
[left, right] = String.split(str, ~r/\s{3}/)
[String.to_integer(left), String.to_integer(right)]
end)
|> Enum.to_list()
|> Enum.reduce(%{left: [], right: []}, fn [left, right], acc ->
acc
|> Map.put(:left, [ left | acc[:left]])
|> Map.put(:right, [ right | acc[:right]])
end)
end
end
RowParser.parse(test_input)
test parser
ExUnit.start(autorun: false)
defmodule RowParserTest do
use ExUnit.Case, async: true
@test_input """
3 4
4 3
2 5
1 3
3 9
3 3
"""
test "row parse test" do
assert RowParser.parse(@test_input) ==
%{left: [3, 3, 1, 2, 4, 3], right: [3, 9, 3, 5, 3, 4]}
end
end
ExUnit.run()
part 1
defmodule PartOne do
def solve(input) do
IO.puts("--- Part One ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
%{left: left, right: right} = input
|> RowParser.parse()
Enum.sort(left)
|> Enum.zip_with(Enum.sort(right), &calc_distance/2)
|> Enum.sum()
end
def calc_distance(l, r) do
abs(l - r)
end
end
part 1 - test
ExUnit.start(autorun: false)
defmodule PartOneTest do
use ExUnit.Case, async: true
@input puzzle_input
@expected 1388114
test "part one" do
assert PartOne.run(@input) == @expected
end
end
ExUnit.run()
part 2
defmodule PartTwo do
def solve(input) do
IO.puts("--- Part Two ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
%{left: left, right: right} = input
|> RowParser.parse()
f_map = Enum.frequencies(right)
left
|> Enum.map(fn num ->
num * Map.get(f_map, num, 0)
end)
|> Enum.sum()
end
end
PartTwo.solve(puzzle_input)
part 2 - test
ExUnit.start(autorun: false)
defmodule PartTwoTest do
use ExUnit.Case, async: true
import PartTwo
@input puzzle_input
@expected 23529853
test "part two" do
assert run(@input) == @expected
end
end
ExUnit.run()