Advent of Code 2022
Mix.install([
{:memoize, "~> 1.4"},
{:tesla, "~> 1.4"},
{:hackney, "~> 1.18"}
])
defmodule Api do
use Tesla
use Memoize
@session System.get_env("AOC_SESSION")
adapter(Tesla.Adapter.Hackney)
plug(Tesla.Middleware.BaseUrl, "https://adventofcode.com/2022/day/")
plug(Tesla.Middleware.Headers, [{"cookie", "session=#{@session}"}])
defmemo get_input(day) do
{:ok, response} = get("#{day}/input")
response.body
end
end
:ok
Task 1
test_input = """
1000
2000
3000
4000
5000
6000
7000
8000
9000
10000
"""
solve1 = fn input ->
input
|> String.split("\n\n")
|> Enum.map(fn cals ->
cals |> String.split("\n", trim: true) |> Enum.map(&String.to_integer/1) |> Enum.sum()
end)
|> Enum.max()
end
24000 = solve1.(test_input)
solve1.(Api.get_input(1)) |> IO.inspect(label: "1.1")
solve2 = fn input ->
input
|> String.split("\n\n")
|> Enum.map(fn cals ->
cals |> String.split("\n", trim: true) |> Enum.map(&String.to_integer/1) |> Enum.sum()
end)
|> Enum.sort(&>=/2)
|> Enum.take(3)
|> Enum.sum()
end
45000 = solve2.(test_input)
solve2.(Api.get_input(1)) |> IO.inspect(label: "1.2")
:ok
Task 2
test_input = """
A Y
B X
C Z
"""
solve1 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.map(fn round ->
String.split(round, " ")
|> case do
["A", "X"] -> 1 + 3
["A", "Y"] -> 2 + 6
["A", "Z"] -> 3 + 0
["B", "X"] -> 1 + 0
["B", "Y"] -> 2 + 3
["B", "Z"] -> 3 + 6
["C", "X"] -> 1 + 6
["C", "Y"] -> 2 + 0
["C", "Z"] -> 3 + 3
end
end)
|> Enum.sum()
end
15 = solve1.(test_input)
solve1.(Api.get_input(2)) |> IO.inspect(label: "2.1")
solve2 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.map(fn round ->
String.split(round, " ")
|> case do
["A", "X"] -> 3 + 0
["A", "Y"] -> 1 + 3
["A", "Z"] -> 2 + 6
["B", "X"] -> 1 + 0
["B", "Y"] -> 2 + 3
["B", "Z"] -> 3 + 6
["C", "X"] -> 2 + 0
["C", "Y"] -> 3 + 3
["C", "Z"] -> 1 + 6
end
end)
|> Enum.sum()
end
12 = solve2.(test_input)
solve2.(Api.get_input(2)) |> IO.inspect(label: "2.2")
:ok
Task 3
test_input = """
vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw
"""
priorities =
[?a..?z, ?A..?Z]
|> Enum.flat_map(fn chars -> Enum.map(chars, &<<&1::utf8>>) end)
|> Enum.with_index(1)
|> Enum.into(%{})
solve1 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.map(fn rucksack ->
with [first_half, second_half] <-
rucksack
|> String.split_at(div(String.length(rucksack), 2))
|> Tuple.to_list()
|> Enum.map(&(&1 |> String.split("", trim: true) |> MapSet.new())),
[common] <- MapSet.intersection(first_half, second_half) |> MapSet.to_list() do
Map.get(priorities, common)
end
end)
|> Enum.sum()
end
157 = solve1.(test_input)
solve1.(Api.get_input(3)) |> IO.inspect(label: "3.1")
solve2 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.chunk_every(3)
|> Enum.map(fn rucksacks ->
with [first, second, third] <-
Enum.map(rucksacks, &(&1 |> String.split("", trim: true) |> MapSet.new())),
[common] <-
first |> MapSet.intersection(second) |> MapSet.intersection(third) |> MapSet.to_list() do
Map.get(priorities, common)
end
end)
|> Enum.sum()
end
70 = solve2.(test_input)
solve2.(Api.get_input(3)) |> IO.inspect(label: "3.2")
:ok
Task 4
test_input = """
2-4,6-8
2-3,4-5
5-7,7-9
2-8,3-7
6-6,4-6
2-6,4-8
"""
solve1 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.map(fn pair_assignments ->
pair_assignments
|> String.split(",")
|> Enum.map(fn elf ->
elf
|> String.split("-")
|> Enum.map(&String.to_integer/1)
|> then(fn [first, last] -> Range.new(first, last) |> Enum.to_list() |> MapSet.new() end)
end)
|> then(fn [first_elf_assignments, second_elf_assignments] ->
MapSet.subset?(first_elf_assignments, second_elf_assignments) or
MapSet.subset?(second_elf_assignments, first_elf_assignments)
end)
end)
|> Enum.count(& &1)
end
2 = solve1.(test_input)
solve1.(Api.get_input(4)) |> IO.inspect(label: "4.1")
solve2 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.map(fn pair_assignments ->
pair_assignments
|> String.split(",")
|> Enum.map(fn elf ->
elf
|> String.split("-")
|> Enum.map(&String.to_integer/1)
|> then(fn [first, last] -> Range.new(first, last) end)
end)
|> then(fn [first_elf_assignments, second_elf_assignments] ->
Range.disjoint?(first_elf_assignments, second_elf_assignments)
end)
end)
|> Enum.count(&(!&1))
end
4 = solve2.(test_input)
solve2.(Api.get_input(4)) |> IO.inspect(label: "4.2")
:ok
Task 5
test_input = """
[D]
[N] [C]
[Z] [M] [P]
1 2 3
move 1 from 2 to 1
move 3 from 1 to 3
move 2 from 2 to 1
move 1 from 1 to 2
"""
parse_input = fn input ->
with [stacks_input, moves_input] <- input |> String.split("\n\n", trim: true),
stacks <-
stacks_input
|> String.split("\n", trim: true)
|> Enum.flat_map(
&(&1
|> String.split("", trim: true)
|> Enum.with_index()
|> Enum.filter(fn {c, _} -> c =~ ~r/[A-Z]/ end))
)
|> Enum.group_by(fn {_, i} -> i end, fn {c, _} -> c end)
|> Map.values()
|> Enum.map(&Enum.reverse/1)
|> Enum.with_index(1)
|> Enum.into(%{}, fn {stack, index} -> {index, stack} end),
moves <-
moves_input
|> String.split("\n", trim: true)
|> Enum.map(
&(~r/\d+/
|> Regex.scan(&1)
|> Enum.map(fn [v] -> String.to_integer(v) end))
) do
{stacks, moves}
end
end
solve1 = fn input ->
{input_stacks, moves} = parse_input.(input)
moves
|> Enum.reduce(input_stacks, fn [count, from, to], stacks ->
{crates_left, crates_to_move} = Enum.split(stacks[from], -count)
stacks
|> Map.put(from, crates_left)
|> Map.update!(to, &(&1 ++ Enum.reverse(crates_to_move)))
end)
|> Map.values()
|> Enum.map(&List.last/1)
|> Enum.join()
end
"CMZ" = solve1.(test_input)
solve1.(Api.get_input(5)) |> IO.inspect(label: "5.1")
solve2 = fn input ->
{input_stacks, moves} = parse_input.(input)
moves
|> Enum.reduce(input_stacks, fn [count, from, to], stack ->
{crates_left, crates_to_move} = Enum.split(stack[from], -count)
stack
|> Map.put(from, crates_left)
|> Map.update!(to, &(&1 ++ crates_to_move))
end)
|> Map.values()
|> Enum.map(&List.last/1)
|> Enum.join()
end
"MCD" = solve2.(test_input)
solve2.(Api.get_input(5)) |> IO.inspect(label: "5.2")
:ok
Task 6
test_inputs = [
"mjqjpqmgbljsphdztnvjfqwrcgsmlb",
"bvwbjplbgvbhsrlpgdmjqwftvncz",
"nppdvjthqldpwncqszvftbrmjlhg",
"nznrnfrfntjfmvfwmzdfjlvtqnbhcprsg",
"zcfzfwzzqfrljwzlrfnpqdbhtmscgvjw"
]
solve = fn input, marker_length ->
input
|> String.to_charlist()
|> Stream.chunk_every(marker_length, 1)
|> Stream.take_while(&(&1 |> Enum.uniq() |> length() != marker_length))
|> Enum.count()
|> Kernel.+(marker_length)
end
[7, 5, 6, 10, 11] = Enum.map(test_inputs, &solve.(&1, 4))
solve.(Api.get_input(6), 4) |> IO.inspect(label: "6.1")
[19, 23, 23, 29, 26] = Enum.map(test_inputs, &solve.(&1, 14))
solve.(Api.get_input(6), 14) |> IO.inspect(label: "6.2")
Task 7
test_input = """
$ cd /
$ ls
dir a
14848514 b.txt
8504156 c.dat
dir d
$ cd a
$ ls
dir e
29116 f
2557 g
62596 h.lst
$ cd e
$ ls
584 i
$ cd ..
$ cd ..
$ cd d
$ ls
4060174 j
8033020 d.log
5626152 d.ext
7214296 k
"""
solve1 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.reduce({[], %{}}, fn
"$ cd ..", {dir_stack, sizes} ->
{tl(dir_stack), sizes}
"$ cd " <> dir, {dir_stack, sizes} ->
uuid = :crypto.strong_rand_bytes(32) |> Base.url_encode64() |> binary_part(0, 32)
{["#{dir}::#{uuid}" | dir_stack], sizes}
command, {dir_stack, sizes} ->
command
|> Integer.parse()
|> case do
{size, _} ->
{dir_stack,
dir_stack
|> Enum.into(%{}, fn dir -> {dir, size} end)
|> Map.merge(sizes, fn _k, v1, v2 -> v1 + v2 end)}
_ ->
{dir_stack, sizes}
end
end)
|> elem(1)
|> Enum.filter(fn {_, size} -> size <= 100_000 end)
|> Enum.map(&elem(&1, 1))
|> Enum.sum()
end
95437 = solve1.(test_input)
solve1.(Api.get_input(7)) |> IO.inspect(label: "7.1")
solve2 = fn input ->
input
|> String.split("\n", trim: true)
|> Enum.reduce({[], %{}}, fn
"$ cd ..", {dir_stack, sizes} ->
{tl(dir_stack), sizes}
"$ cd " <> dir, {dir_stack, sizes} ->
uuid = :crypto.strong_rand_bytes(32) |> Base.url_encode64() |> binary_part(0, 32)
{["#{dir}::#{uuid}" | dir_stack], sizes}
command, {dir_stack, sizes} ->
command
|> Integer.parse()
|> case do
{size, _} ->
{dir_stack,
dir_stack
|> Enum.into(%{}, fn dir -> {dir, size} end)
|> Map.merge(sizes, fn _k, v1, v2 -> v1 + v2 end)}
_ ->
{dir_stack, sizes}
end
end)
|> elem(1)
|> Enum.sort_by(fn {_, size} -> -size end)
|> then(fn [{_, all} | rest] ->
rest
|> Enum.reverse()
|> Enum.find(fn {_, size} -> 70_000_000 - all + size > 30_000_000 end)
|> elem(1)
end)
end
24_933_642 = solve2.(test_input)
solve2.(Api.get_input(7)) |> IO.inspect(label: "7.2")
:ok
Task 8
test_input = """
30373
25512
65332
33549
35390
"""
solve1 = fn input ->
with trees_rows <- String.split(input, "\n", trim: true),
size <- length(trees_rows),
trees <-
trees_rows
|> Enum.flat_map(fn row ->
row |> String.split("", trim: true) |> Enum.map(&String.to_integer/1)
end)
|> Enum.with_index() do
trees
|> Enum.map(fn {curr_height, curr_idx} ->
row_idx = trunc(curr_idx / size)
col_idx = rem(curr_idx, size)
top = Enum.slice(trees, col_idx..curr_idx//size) |> Enum.reverse() |> tl
left = Enum.slice(trees, (row_idx * size)..curr_idx) |> Enum.reverse() |> tl
right = Enum.slice(trees, curr_idx..(row_idx * size + size - 1)) |> tl
bottom = Enum.slice(trees, curr_idx..(size * size)//size) |> tl
Enum.any?([top, left, right, bottom], fn trees_slice ->
trees_slice == [] || Enum.all?(trees_slice, &(elem(&1, 0) < curr_height))
end)
end)
|> Enum.count(& &1)
end
end
21 = solve1.(test_input)
solve1.(Api.get_input(8)) |> IO.inspect(label: "8.1")
solve2 = fn input ->
with trees_rows <- String.split(input, "\n", trim: true),
size <- length(trees_rows),
trees <-
trees_rows
|> Enum.flat_map(fn row ->
row |> String.split("", trim: true) |> Enum.map(&String.to_integer/1)
end)
|> Enum.with_index() do
trees
|> Enum.map(fn {curr_height, curr_idx} ->
row_idx = trunc(curr_idx / size)
col_idx = rem(curr_idx, size)
top = Enum.slice(trees, col_idx..curr_idx//size) |> Enum.reverse() |> tl
left = Enum.slice(trees, (row_idx * size)..curr_idx) |> Enum.reverse() |> tl
right = Enum.slice(trees, curr_idx..(row_idx * size + size - 1)) |> tl
bottom = Enum.slice(trees, curr_idx..(size * size)//size) |> tl
Enum.map([top, left, right, bottom], fn trees_slice ->
Enum.reduce_while(trees_slice, 0, fn
{height, _}, score when height >= curr_height -> {:halt, score + 1}
_, score -> {:cont, score + 1}
end)
end)
|> Enum.product()
end)
|> Enum.max()
end
end
8 = solve2.(test_input)
solve2.(Api.get_input(8)) |> IO.inspect(label: "8.2")
:ok
Task 9
defmodule Task9 do
def solve1(input) do
input
|> parse_input
|> Enum.reduce(
[{0, 0}, {0, 0}, MapSet.new([{0, 0}])],
fn step, [head, tail, tail_positions] ->
new_head = move_head(head, step)
new_tail = move_tail(new_head, tail, step)
[new_head, new_tail, MapSet.put(tail_positions, new_tail)]
end
)
|> then(fn [_, _, tail_positions] -> Enum.count(tail_positions) end)
end
defp parse_input(input) do
input
|> String.split("\n", trim: true)
|> Enum.flat_map(
&(&1
|> String.split(" ")
|> then(fn [direction, count] ->
List.duplicate(direction, String.to_integer(count))
end))
)
end
defp move_head({x, y}, "R"), do: {x + 1, y}
defp move_head({x, y}, "U"), do: {x, y + 1}
defp move_head({x, y}, "L"), do: {x - 1, y}
defp move_head({x, y}, "D"), do: {x, y - 1}
defp move_tail({h_x, h_y} = _head, {t_x, t_y} = tail, step) do
cond do
t_x == h_x and t_y - h_y == 2 ->
{t_x, t_y - 1}
t_y == h_y and h_x - t_x == 2 ->
{t_x + 1, t_y}
t_x == h_x and h_y - t_y == 2 ->
{t_x, t_y + 1}
t_y == h_y and t_x - h_x == 2 ->
{t_x - 1, t_y}
t_y - h_y == 2 or h_x - t_x == 2 or h_y - t_y == 2 or t_x - h_x == 2 ->
case step do
"R" -> {h_x - 1, h_y}
"U" -> {h_x, h_y - 1}
"L" -> {h_x + 1, h_y}
"D" -> {h_x, h_y + 1}
end
true ->
tail
end
end
end
test_input = """
R 4
U 4
L 3
D 1
R 4
D 1
L 5
R 2
"""
13 = Task9.solve1(test_input)
Task9.solve1(Api.get_input(9)) |> IO.inspect(label: "9.1")
:ok