advent_of_code/elixir/livebook/2022/aoc_day_11.livemd
2024-12-01 21:10:14 -05:00

3 KiB

Year 2022 Day11

Section

parse_op = fn
  "Operation: new = " <> z, f, s -> f.(z, f, s)
  "old " <> z, f, s -> f.(z, f, [:old | s])
  "old" <> z, f, s -> f.(z, f, [:old | s])
  "+ " <> z, f, s -> f.(z, f, [:plus | s])
  "- " <> z, f, s -> f.(z, f, [:minus | s])
  "* " <> z, f, s -> f.(z, f, [:mul | s])
  "/ " <> z, f, s -> f.(z, f, [:div | s])
  "", _, s -> s |> Enum.reverse()
  n, f, s ->
    [a | b] = String.split( n, " ")
    a = String.trim(a) |> String.to_integer()
    f.(b |> Enum.join(" "), f, [a | s])
end
subs = fn b, a -> if b == :old, do: a, else: b end
resolve_stack = fn 
  old, [a, :plus, b] -> subs.(a, old) + subs.(b, old)
  old, [a, :minus, b] -> subs.(a, old) - subs.(b, old)
  old, [a, :mul, b] -> subs.(a, old) * subs.(b, old)
  old, [a, :div, b] -> subs.(a, old) / subs.(b, old)
end
defmodule Monkey do
  defstruct id: nil, items: nil, op: nil, mod: nil, if_true: nil, if_false: nil, inspections: 0
end
defmodule MonkeyMachine do
  
  def load(instructions) do
    
    {barrel, _} = for instruction <- instructions, reduce: {%{}, nil} do
      {barrel, monkey} -> 
        (fn 
          {:break} ->
          {barrel |> Map.put(monkey.id, monkey), nil}
          {:add_monkey, id} -> {barrel, %Monkey{id: id}}
          {:starting_items, i} -> {barrel, %{monkey | items: i }}
          {a, o} -> {barrel, %{monkey | a => o }}
        end).(instruction)
    end

    barrel
  end

  def run(barrel) do
    run(barrel, 0, (Map.keys(barrel) |> Enum.count()) - 1) # Map.values(barrel) |> Stream.map(&(&1.mod)) |> Enum.product())
  end

  defp run(barrel, i, n) when i > n do
    barrel
  end

  defp run(barrel, id, n) do
    monkey = Map.get(barrel, id)
    barrel = for item <- monkey.items, reduce: barrel do
      b ->
        
        r = div(monkey.op.(item), 3)
        dest = if rem(r, monkey.mod) == 0, do: monkey.if_true, else: monkey.if_false
        
        b 
          |> Map.update!(dest, fn d -> %{d | items: [r | d.items]} end)
          |> Map.update!(id, fn m -> Map.update!(m, :inspections, &(&1 + 1)) end)
        end |> Map.update!(id, fn m -> %{m | items: []} end)
    
    run(barrel, id + 1, n)
  end
end
barrel = input
|> String.split("\n")
|> Stream.map(&String.trim/1)
|> Enum.map(
  fn 
    "Monkey " <> z -> {:add_monkey, z |> String.split(":") |> Enum.at(0) |> String.to_integer()}
    "Starting items: " <> i -> {:starting_items, i |> String.split(",", trim: true) |> Enum.map(&String.to_integer(String.trim(&1)))}
    "Operation: " <> _ = u -> {:op, fn o -> resolve_stack.(o, parse_op.(u, parse_op, [])) end}
    "Test: divisible by " <> n -> {:mod, String.to_integer(n)}
    "If true: throw to monkey " <> n -> {:if_true, String.to_integer(n)}
    "If false: throw to monkey " <> n -> {:if_false, String.to_integer(n)}
    "" -> {:break}
    u -> u
  end
)
|> MonkeyMachine.load()

for _ <- 1..20, reduce: barrel do
  b ->
  MonkeyMachine.run(b)
end 
|> Map.values() 
|> Stream.map(&(&1.inspections)) 
|> Enum.sort(:desc) 
|> Enum.take(2) 
|> Enum.product()