Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Day 19

2022/day19.livemd

Day 19

Mix.install([
  {:kino_aoc, git: "https://github.com/ljgago/kino_aoc"}
])
:ok

Section

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2022", "19", System.fetch_env!("LB_ADVENT_OF_CODE_SESSION"))
{:ok,
 "Blueprint 1: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 15 clay. Each geode robot costs 2 ore and 8 obsidian.\nBlueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 17 clay. Each geode robot costs 3 ore and 10 obsidian.\nBlueprint 3: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 20 clay. Each geode robot costs 2 ore and 14 obsidian.\nBlueprint 4: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 4 ore and 15 obsidian.\nBlueprint 5: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 13 clay. Each geode robot costs 3 ore and 15 obsidian.\nBlueprint 6: Each ore robot costs 2 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 15 clay. Each geode robot costs 2 ore and 7 obsidian.\nBlueprint 7: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 9 clay. Each geode robot costs 3 ore and 7 obsidian.\nBlueprint 8: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 2 ore and 8 obsidian.\nBlueprint 9: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 20 clay. Each geode robot costs 4 ore and 18 obsidian.\nBlueprint 10: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 11 clay. Each geode robot costs 2 ore and 19 obsidian.\nBlueprint 11: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 7 clay. Each geode robot costs 3 ore and 10 obsidian.\nBlueprint 12: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 11 clay. Each geode robot costs 2 ore and 16 obsidian.\nBlueprint 13: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 16 clay. Each geode robot costs 3 ore and 15 obsidian.\nBlueprint 14: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 3 ore and 13 obsidian.\nBlueprint 15: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 13 clay. Each geode robot costs 2 ore and 20 obsidian.\nBlueprint 16: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 14 clay. Each geode robot costs 4 ore and 10 obsidian.\nBlueprint 17: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 17 clay. Each geode robot costs 3 ore and 16 obsidian.\nBlueprint 18: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 20 clay. Each geode robot costs 2 ore and 17 obsidian.\nBlueprint 19: Each ore robot costs 2 ore. Each clay robot costs 4 ore. Each obsidian robot costs 2 ore and 16 clay. Each geode robot costs 4 ore and 12 obsidian.\nBlueprint 20: Each ore robot costs 3 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 16 clay. Each geode robot costs 3 ore and 20 obsidian.\nBlueprint 21: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 4 ore and 12 obsidian.\nBlueprint 22: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 3 ore and 13 clay. Each geode robot costs 3 ore and 19 obsidian.\nBlueprint 23: Each ore robot costs 3 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 18 clay. Each geode robot costs 3 ore and 8 obsidian.\nBlueprint 24: Each ore robot costs 4 ore. Each clay robot costs 3 ore. Each obsidian robot costs 2 ore and 13 clay. Each geode robot costs 2 ore and 9 obsidian.\nBlueprint 25: Each ore robot costs 4 ore. Each clay robot costs 4 ore. Each obsidian robot costs 4 ore and 5 clay. Each geode robot costs 3 ore and 15 obsidian.\nBlueprint 26: Each ore robot costs 4 ore. Each clay robot costs " <> ...}
puzzle_input = """
Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.
Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.
"""
"Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.\nBlueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian.\n"
regex =
  ~r/ore robot.*(?\d+) ore.*clay robot.*(?\d+) ore.*obsidian robot.*(?\d+) ore and (?\d+) clay.*geode robot.*(?\d+) ore and (?\d+) obsidian/

plans =
  puzzle_input
  |> String.split("\n", trim: true)
  |> Kino.render()
  |> Enum.map(fn line ->
    blueprint =
      Regex.named_captures(regex, line)
      |> Map.new(fn {k, v} ->
        {String.to_atom(k), String.to_integer(v)}
      end)

    %{
      ore: %{ore: blueprint.ore_ore},
      clay: %{ore: blueprint.clay_ore},
      obsidian: %{ore: blueprint.obsidian_ore, clay: blueprint.obsidian_clay},
      geode: %{ore: blueprint.geode_ore, obsidian: blueprint.geode_obsidian},
      max: %{
        ore:
          Enum.max([
            blueprint.ore_ore,
            blueprint.clay_ore,
            blueprint.obsidian_ore,
            blueprint.geode_ore
          ]),
        clay: blueprint.obsidian_clay,
        obsidian: blueprint.geode_obsidian
      }
    }
  end)
["Blueprint 1: Each ore robot costs 4 ore. Each clay robot costs 2 ore. Each obsidian robot costs 3 ore and 14 clay. Each geode robot costs 2 ore and 7 obsidian.",
 "Blueprint 2: Each ore robot costs 2 ore. Each clay robot costs 3 ore. Each obsidian robot costs 3 ore and 8 clay. Each geode robot costs 3 ore and 12 obsidian."]
[
  %{
    clay: %{ore: 2},
    geode: %{obsidian: 7, ore: 2},
    max: %{clay: 14, obsidian: 7, ore: 4},
    obsidian: %{clay: 14, ore: 3},
    ore: %{ore: 4}
  },
  %{
    clay: %{ore: 3},
    geode: %{obsidian: 12, ore: 3},
    max: %{clay: 8, obsidian: 12, ore: 3},
    obsidian: %{clay: 8, ore: 3},
    ore: %{ore: 2}
  }
]
defmodule Blueprint do
  def produce(plan, time, machines) do
    produce(plan, time, machines, %{ore: 0, clay: 0, obsidian: 0, geode: 0})
  end

  def produce(_plan, 0, machines, stash) do
    %{machines: machines, stash: stash}
  end

  def produce(plan, time_left, machines, stash) do
    do_shopping(plan, stash)
    |> Enum.filter(fn {k, _} -> (machines[k] || 0) < plan.max[k] end)
    |> Enum.flat_map(fn {machine, new_stash} ->
      # if machine in [:obsidian, :geode], do: IO.inspect(new_stash)
      [
        produce(
          plan,
          time_left - 1,
          add_machine(machine, machines),
          update_stash(machines, new_stash)
        )
      ]
    end)
    # |> tap(fn list ->
    #   Enum.filter(list, & &1.obsidian > 0)
    #   |> Enum.map(&IO.inspect/1)
    # end)
    |> Enum.max_by(&amp; &amp;1.stash.geode)
  end

  defp add_machine(nil, machines), do: machines
  defp add_machine(name, machines), do: Map.update(machines, name, 1, &amp;(&amp;1 + 1))

  defp update_stash(machines, stash) do
    Enum.reduce(machines, stash, fn {k, v}, acc ->
      Map.update(acc, k, v, &amp;(&amp;1 + v))
    end)
  end

  def do_shopping(plan, stash)
      when stash.ore >= plan.geode.ore and stash.obsidian >= plan.geode.obsidian,
      do: [{:geode, buy(plan.geode, stash)}]

  def do_shopping(plan, stash)
      when stash.ore >= plan.obsidian.ore and stash.clay >= plan.obsidian.clay,
      do: [{:obsidian, buy(plan.obsidian, stash)}, {nil, stash}]

  def do_shopping(plan, stash) do
    [
      {nil, stash}
      | Enum.flat_map([:ore, :clay], fn k ->
          if stash.ore >= plan[k].ore do
            [{k, %{stash | ore: stash.ore - plan[k].ore}}]
          else
            []
          end
        end)
    ]
  end

  defp buy(price, stash) do
    Map.merge(stash, price, fn _k, v1, v2 -> v1 - v2 end)
  end
end
{:module, Blueprint, <<70, 79, 82, 49, 0, 0, 22, ...>>, {:buy, 2}}
plan = hd(plans) |> Kino.inspect()
stash = %{clay: 35, geode: 0, obsidian: 0, ore: 4}

Blueprint.do_shopping(plan, stash)
%{
  clay: %{ore: 2},
  geode: %{obsidian: 7, ore: 2},
  max: %{clay: 14, obsidian: 7, ore: 4},
  obsidian: %{clay: 14, ore: 3},
  ore: %{ore: 4}
}
[
  obsidian: %{clay: 21, geode: 0, obsidian: 0, ore: 1},
  nil: %{clay: 35, geode: 0, obsidian: 0, ore: 4}
]
plans
# |> Enum.drop(1)
|> Enum.take(1)
|> Kino.inspect()
|> Enum.map(fn plan -> Blueprint.produce(plan, 24, %{ore: 1}) end)
[
  %{
    clay: %{ore: 2},
    geode: %{obsidian: 7, ore: 2},
    max: %{clay: 14, obsidian: 7, ore: 4},
    obsidian: %{clay: 14, ore: 3},
    ore: %{ore: 4}
  }
]