Advent of Code 2022 - Day 3
Mix.install([
{:req_aoc, github: "mcrumm/req_aoc", ref: "28e3813"}
])
Puzzle description
Day 3: Rucksack Reorganization.
Note: I grew tired of downloading my user input, so I figured out how to download the input from the Advent of Code site. You need to get your session cookie on adventofcode.com. Use DevTools or similar and inspect the Request Headers to find your session id and then add it to Livebook secrets as AOC_SESSION
.
Input
defmodule Input do
def load! do
ReqAOC.fetch!({2022, 03, System.fetch_env!("LB_AOC_SESSION")}, max_retries: 0)
end
@doc """
Splits a string by newlines.
"""
def to_lines(input) when is_binary(input) do
input
|> String.trim("\n")
|> String.split("\n")
end
end
Solution
defmodule Part1 do
# preflight:
## split string into lines (without break characters)
def run(input) do
input
|> Enum.map(fn line ->
# split the rucksack into two equal compartments
{head, tail} = String.split_at(line, div(String.length(line), 2))
head
|> String.codepoints()
|> Enum.reduce_while(:ok, fn code, _ ->
case :binary.match(tail, code) do
{_, 1} -> {:halt, code |> priority()}
:nomatch -> {:cont, :ok}
end
end)
end)
|> Enum.sum()
end
lower = Enum.into(?a..?z, [])
upper = Enum.into(?A..?Z, [])
alphabet = lower ++ upper
def priority(x) when is_binary(x) do
x |> :binary.first() |> priority()
end
for {c, k} <- Enum.with_index(alphabet, 1) do
def priority(unquote(c)), do: unquote(k)
end
end
defmodule Part2 do
# preflight:
## split string into lines (without break characters)
def run(input) do
# chunk lines into groups of three
# find codepoint intersecting all three lines
# convert to char value
# sum
input
|> Enum.chunk_every(3)
|> Enum.map(fn [a, b, c] ->
# convert a to codepoints
# look for match in b
a
|> String.codepoints()
|> Enum.reduce_while(nil, fn code, acc ->
# find match in b
case :binary.match(b, code) do
{_, _} ->
# find match in c
case :binary.match(c, code) do
{_, _} ->
# success! return the priority value
{:halt, Part1.priority(code)}
:nomatch ->
# continue to next character
{:cont, acc}
end
:nomatch ->
# continue to next character
{:cont, acc}
end
end)
end)
|> Enum.sum()
end
end
ExUnit.start(autorun: false)
defmodule Test do
use ExUnit.Case, async: true
@example_input ~s(
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
)
@input Input.load!()
test "priority" do
for {l, k} <- Enum.with_index(?a..?z, 1) do
assert Part1.priority(l) == k
end
for {u, k} <- Enum.with_index(?A..?Z, 27) do
assert Part1.priority(u) == k
end
end
test "part 1" do
assert @example_input |> Input.to_lines() |> Part1.run() === 157
@input |> Input.to_lines() |> Part1.run() |> dbg()
end
test "part 2" do
assert @example_input |> Input.to_lines() |> Part2.run() === 70
@input |> Input.to_lines() |> Part2.run() |> dbg()
end
end
ExUnit.configure(trace: true)
ExUnit.run()