diff --git a/2022-elixir/lib/solutions/day_11.ex b/2022-elixir/lib/solutions/day_11.ex new file mode 100644 index 0000000..e7fb9b8 --- /dev/null +++ b/2022-elixir/lib/solutions/day_11.ex @@ -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 diff --git a/2022-elixir/test/solutions/day_11_test.exs b/2022-elixir/test/solutions/day_11_test.exs new file mode 100644 index 0000000..a6ca16f --- /dev/null +++ b/2022-elixir/test/solutions/day_11_test.exs @@ -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