AoC 2022 Day 11
Mix.install([:kino])
defmodule Utils do
def split(line, sep \\ "") do
String.split(line, sep, trim: true)
end
def split_all_lines(text, sep \\ "") do
text
|> String.split("\n", trim: true)
|> Enum.map(&split(&1, sep))
end
def to_numbers(number) when is_binary(number) do
String.to_integer(number)
end
def to_numbers(numbers) when is_list(numbers) do
Enum.map(numbers, &to_numbers/1)
end
def to_matrix(text, sep \\ "") do
text
|> split_all_lines(sep)
|> then(fn data ->
for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
{{r, c}, col}
end
end)
|> Map.new()
end
end
Setup
import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
data =
split(text, "\n\n")
|> Enum.map(fn chunk ->
Regex.run(
~r/Monkey (\d+):\s+Starting items: ([\d, ]+)\s+Operation: (.+)\n\s+Test: divisible by (\d+)\n\s+If true: throw to monkey (\d+)\n\s+If false: throw to monkey (\d+)/,
chunk
)
|> then(fn [_, id, starting, change, div_by, true_to, false_to] ->
id = String.to_integer(id)
starting = starting |> String.split(", ") |> to_numbers()
div_by = String.to_integer(div_by)
true_to = String.to_integer(true_to)
false_to = String.to_integer(false_to)
{id,
%{
items: starting,
change: change,
div_by: div_by,
true_to: true_to,
false_to: false_to,
count: 0
}}
end)
end)
|> Map.new()
P1
defmodule P1 do
def run(data) do
Enum.reduce(0..(map_size(data) - 1), data, fn id, data ->
%{
items: items,
change: change,
div_by: div_by,
true_to: true_to,
false_to: false_to
} = data[id]
data = update_in(data, [id, :count], &(&1 + length(items)))
items =
Enum.map(items, fn item ->
{new, _} = Code.eval_string(change, old: item)
div(new, 3)
end)
data =
Enum.reduce(items, data, fn item, data ->
if rem(item, div_by) == 0 do
update_in(data, [true_to, :items], &(&1 ++ [item]))
else
update_in(data, [false_to, :items], &(&1 ++ [item]))
end
end)
update_in(data, [id, :items], fn _ -> [] end)
end)
end
end
Enum.reduce(1..20, data, fn _, data -> P1.run(data) end)
|> Map.values()
|> Enum.map(& &1.count)
|> Enum.sort()
|> Enum.take(-2)
|> Enum.product()
P2
defmodule P2 do
def run(data, div_product) do
Enum.reduce(0..(map_size(data) - 1), data, fn id, data ->
%{
items: items,
change: change,
div_by: div_by,
true_to: true_to,
false_to: false_to
} = data[id]
data = update_in(data, [id, :count], &(&1 + length(items)))
items =
Enum.map(items, fn item ->
{new, _} = Code.eval_string(change, old: item)
rem(new, div_product)
end)
data =
Enum.reduce(items, data, fn item, data ->
if rem(item, div_by) == 0 do
update_in(data, [true_to, :items], &[item | &1])
else
update_in(data, [false_to, :items], &[item | &1])
end
end)
update_in(data, [id, :items], fn _ -> [] end)
end)
end
end
div_product = data |> Map.values() |> Enum.map(& &1.div_by) |> Enum.product()
Enum.reduce(1..10000, data, fn _, data -> P2.run(data, div_product) end)
|> Map.values()
|> Enum.map(& &1.count)
|> Enum.sort()
|> Enum.take(-2)
|> Enum.product()