Add WIP for 2022:11 "Monkey in the Middle"

This commit is contained in:
Anders Englöf Ytterström 2022-12-12 14:27:46 +01:00 committed by Anders Englöf Ytterström
parent 4420dfa522
commit 102982a6f0
2 changed files with 247 additions and 0 deletions

View file

@ -0,0 +1,196 @@
defmodule Aoc.Solution.Day11 do
import Aoc.Utils
@turns 20
@name "Day 11: Monkey in the Middle"
@behaviour Solution
@impl Solution
def get_name, do: @name
@impl Solution
def present(solution), do: "Monkey business is #{solution} (50616)"
@impl Solution
def present_again(solution), do: "Solution is #{solution}"
@impl Solution
def parse!(raw) do
raw
|> parse_values("\n\n")
|> into_monkeys()
end
@impl Solution
def solve(monkeys) do
monkeys
|> take_turns()
|> investigate()
|> monkey_biz()
end
@impl Solution
def solve_again(_input) do
"(TBW)"
end
def into_monkeys(monkeys) when is_list(monkeys) do
Enum.map(monkeys, &monkey/1)
end
def monkey([], ms) when is_map(ms) do
ms
end
def monkey(["Starting items: " <> items | data], ms) do
monkey(data, %{ms | items: parse_values(items, ", ")})
end
def monkey(["Operation: new = " <> f | data], ms) do
monkey(data, %{ms | op: operation(f)})
end
def monkey(["Test: divisible by " <> d | data], ms) do
monkey(data, %{ms | test: String.to_integer(d)})
end
def monkey(["If true: throw to monkey " <> mid | data], ms) do
monkey(data, %{ms | pass: String.to_integer(mid)})
end
def monkey(["If false: throw to monkey " <> mid | data], ms) do
monkey(data, %{ms | fail: String.to_integer(mid)})
end
def monkey(["Monkey " <> mid | data]) do
mid = String.first(mid) |> String.to_integer()
monkey(data, %{id: mid, items: [], op: nil, test: 0, pass: 0, fail: 0, count: 0})
end
def monkey(text) do
monkey(split_lines(text))
end
def operation("old * old") do
{&oo/2, nil}
end
def operation("old * " <> x) do
{&o_times_x/2, String.to_integer(x)}
end
def operation("old + " <> x) do
{&o_plus_x/2, String.to_integer(x)}
end
def oo(x, _) when is_binary(x) do
oo(String.to_integer(x), nil)
end
def oo(x, _), do: x * x
def o_plus_x(x, y) when is_binary(x) do
String.to_integer(x) + y
end
def o_plus_x(x, y), do: x + y
def o_times_x(x, y) when is_binary(x) do
String.to_integer(x) * y
end
def o_times_x(x, y), do: x * y
def take_turns(monkeys, n \\ 0)
def take_turns(monkeys, @turns) do
monkeys
end
def take_turns(monkeys, n) do
monkeys
|> take_turns(n + 1)
|> exchange_items()
end
def monkey_biz(monkeys) do
[a, b | _] =
monkeys
|> Enum.map(fn %{count: count} -> count end)
|> Enum.sort()
|> Enum.reverse()
a * b
end
def exchange_items(monkeys) do
monkeys
|> throw_items()
|> receive_items()
|> investigate()
end
def throw_items(monkeys) when is_list(monkeys) do
{
Enum.reduce(monkeys, [], &throw_items/2),
monkeys
}
end
def throw_items(%{id: mid, items: items, op: {f, a}, test: test, pass: pass, fail: fail}, acc) do
q =
Enum.filter(acc, fn {to, _v, _f, _s} -> to == mid end)
|> Enum.map(fn {_t, v, _f, _s} -> v end)
acc =
Enum.map(acc, fn t = {to, v, f, s} ->
case Enum.member?(q, v) do
true -> {to, v, f, s + 1}
false -> t
end
end)
throws =
for item <- items ++ q do
wl = f.(item, a) |> div(3)
case rem(wl, test) do
0 -> {pass, wl, mid, 0}
_ -> {fail, wl, mid, 0}
end
end
throws ++ acc
end
def receive_items({throws, monkeys}) do
Enum.map(monkeys, fn monkey = %{id: mid, count: count} ->
c =
Enum.filter(throws, fn {_to, _v, from, _} -> from == mid end)
|> Enum.count()
%{
monkey
| items:
throws
|> Enum.filter(fn {to, _v, _from, s} -> s < 1 and to == mid end)
|> Enum.map(fn {_t, v, _f, _s} -> v end),
count: count + c
}
end)
end
def investigate(monkeys) do
Enum.map(monkeys, fn %{id: id, count: count, items: items} ->
{
id,
count,
items
}
end)
|> IO.inspect()
monkeys
end
end

View file

@ -0,0 +1,51 @@
defmodule Day11Test do
use ExUnit.Case
doctest Aoc.Solution.Day11
import Aoc.Solution.Day11
@input ~s(
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1
)
test "11: Monkey in the Middle, part 1" do
expected = 10605
result = @input |> parse!() |> solve()
assert result == expected
end
# test "11: Monkey in the Middle, part 2" do
# expected = :something
# result = @input |> parse!() |> solve_again()
# assert result == expected
# end
end