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

Day 02

day_02.livemd

Day 02

Mix.install([
  {:kino_aoc, "~> 0.1"},
  {:nimble_parsec, "~> 1.4"}
])

Input

{:ok, raw_input} =
  KinoAOC.download_puzzle("2023", "2", System.fetch_env!("LB_AOC_SESSION"))
defmodule Parser do
  import NimbleParsec

  @prefix ignore(string("Game"))
          |> ignore(string(" "))
          |> integer(min: 1)
          |> ignore(string(":"))
          |> unwrap_and_tag(:game_number)

  @color choice([
           string("blue") |> replace(:BLUE),
           string("red") |> replace(:RED),
           string("green") |> replace(:GREEN)
         ])

  @cube optional(ignore(string(" ")))
        |> integer(min: 1)
        |> ignore(string(" "))
        |> concat(@color)
        |> wrap
        |> map({List, :to_tuple, []})

  @draw @cube
        |> repeat(
          concat(
            ignore(string(", ")),
            @cube
          ),
          min: 0
        )
        |> wrap

  @draws @draw
         |> repeat(
           concat(
             ignore(string(";")),
             @draw
           ),
           min: 0
         )
         |> tag(:draws)

  @game @prefix |> concat(@draws)

  defparsec(:parse_line, @game)

  def parse(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&__MODULE__.parse_line/1)
    |> Enum.map(&elem(&1, 1))
  end
end
input = Parser.parse(raw_input)
input_example =
  Parser.parse("""
  Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
  Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
  Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
  Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
  Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
  """)

Part 1

defmodule Part1 do
  def find_largest(game) do
    game
    |> Keyword.fetch!(:draws)
    |> List.flatten()
    |> Enum.group_by(&elem(&1, 1), &elem(&1, 0))
    |> Enum.map(fn {key, val} ->
      {key, Enum.max(val)}
    end)
    |> Enum.into(%{})
  end

  def is_possible(game, max \\ %{RED: 12, GREEN: 13, BLUE: 14}) do
    largest = find_largest(game)

    largest[:RED] <= max[:RED] and
      largest[:GREEN] <= max[:GREEN] and
      largest[:BLUE] <= max[:BLUE]
  end

  def calculate(input) do
    input
    |> Enum.filter(&amp;is_possible(&amp;1))
    |> Enum.map(&amp;Keyword.fetch!(&amp;1, :game_number))
    |> Enum.sum()
  end
end
Part1.calculate(input_example)
Part1.calculate(input)

Part 2

defmodule Part2 do
  def calculate_power(largest) do
    largest[:RED] * largest[:GREEN] * largest[:BLUE]
  end

  def calculate(input) do
    input
    |> Enum.map(&amp;Part1.find_largest/1)
    |> Enum.map(&amp;calculate_power/1)
    |> Enum.sum()
  end
end
Part2.calculate(input_example)
Part2.calculate(input)