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

AoC 2023 Day 2

2023/day2.livemd

AoC 2023 Day 2

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

Input

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "2", System.fetch_env!("LB_AOC_COOKIE_SECRET"))

Parsing

defmodule CubeGame do
  import NimbleParsec

  defstruct [:id, :sets]

  spacing =
    ignore(optional(string(",")))
    |> ignore(string(" "))

  cube_set =
    spacing
    |> integer(min: 1)
    |> ignore(string(" "))
    |> ascii_string([?a..?z], min: 1)
    |> tag(:cubes)

  game_set =
    times(cube_set, min: 1)
    |> ignore(optional(string(";")))
    |> tag(:set)

  game =
    ignore(string("Game "))
    |> integer(min: 1)
    |> unwrap_and_tag(:id)
    |> ignore(string(":"))
    |> times(game_set, min: 1)
    |> tag(:game)

  games =
    repeat(
      concat(
        game,
        ignore(optional(ascii_char([?\n])))
      )
    )

  defparsecp(:parse, games)

  def parse_input(input) do
    {:ok, games, _, _, _, _} = parse(input)

    games
    |> Enum.map(fn {:game, g} ->
      sets =
        Keyword.get_values(g, :set)
        |> Enum.map(fn s ->
          Keyword.get_values(s, :cubes)
          |> Enum.map(fn [n, name] -> {String.to_atom(name), n} end)
        end)

      %CubeGame{
        id: g[:id],
        sets: sets
      }
    end)
  end
end
games = CubeGame.parse_input(puzzle_input)

Part 1

limits = %{
  red: 12,
  green: 13,
  blue: 14
}

amount_under_limit? = fn {colour, n} ->
  limits[colour] >= n
end

set_possible? = fn set ->
  Enum.all?(set, amount_under_limit?)
end

game_possible? = fn %CubeGame{sets: sets} ->
  Enum.all?(sets, set_possible?)
end
games
|> Enum.filter(game_possible?)
|> Enum.map(&Map.get(&1, :id))
|> Enum.sum()

Part 2

games
|> Enum.map(&Map.get(&1, :sets))
|> Enum.map(fn sets ->
  sets
  |> List.flatten()
  |> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
  |> Enum.map(fn {colour, counts} ->
    {colour, Enum.max(counts)}
  end)
end)
|> Enum.map(fn maximums ->
  maximums
  |> Keyword.values()
  |> Enum.product()
end)
|> Enum.sum()