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

Day 2: Cube Conundrum

2023/day-02.livemd

Day 2: Cube Conundrum

Mix.install([{:kino, "~> 0.11.3"}])

Day 2

sample_input = Kino.Input.textarea("Paste Sample Input")
real_input = Kino.Input.textarea("Paste Real Input")
defmodule CubeGame.Draw do
  defstruct red: 0, blue: 0, green: 0

  @draw_pattern ~r/,? ?(\d+) (red|green|blue)/

  def parse(input) do
    @draw_pattern
    |> Regex.scan(input)
    |> Enum.reduce(%__MODULE__{}, fn [_, count, color], draw ->
      Map.put(draw, String.to_existing_atom(color), String.to_integer(count))
    end)
  end

  def spans?(%__MODULE__{} = superset, %__MODULE__{} = subset) do
    subset.red <= superset.red and
      subset.blue <= superset.blue and
      subset.green <= superset.green
  end

  def min_span(%__MODULE__{} = one, %__MODULE__{} = two) do
    %__MODULE__{
      red: max(one.red, two.red),
      blue: max(one.blue, two.blue),
      green: max(one.green, two.green)
    }
  end

  def power(%__MODULE__{} = draw), do: draw.red * draw.blue * draw.green
end

defmodule CubeGame do
  defstruct id: nil, draws: []

  def parse("Game " <> rest) do
    {id, ": " <> draw_inputs} = Integer.parse(rest)

    draws =
      draw_inputs
      |> String.split(";", trim: true)
      |> Enum.map(&amp;CubeGame.Draw.parse/1)

    %__MODULE__{id: id, draws: draws}
  end

  def min_bag(%__MODULE__{} = game) do
    Enum.reduce(game.draws, %CubeGame.Draw{}, &amp;CubeGame.Draw.min_span/2)
  end

  def possible?(%__MODULE__{} = game, %CubeGame.Draw{} = bag_contents) do
    Enum.all?(game.draws, &amp;CubeGame.Draw.spans?(bag_contents, &amp;1))
  end
end
part_1_bag = %CubeGame.Draw{red: 12, green: 13, blue: 14}

part_1 = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;CubeGame.parse/1)
  |> Enum.filter(&amp;CubeGame.possible?(&amp;1, part_1_bag))
  |> Enum.map(&amp; &amp;1.id)
  |> Enum.sum()
end
part_1.(sample_input)
part_1.(real_input)
part_2 = fn input ->
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&amp;CubeGame.parse/1)
  |> Enum.map(fn game -> game |> CubeGame.min_bag() |> CubeGame.Draw.power() end)
  |> Enum.sum()
end
part_2.(sample_input)
part_2.(real_input)