Advent Of Code 2022 - Day 4

I am getting the feeling that Advent of Code is written from the mindset of primarily imperative languages, because it feels they’re getting easier with Elixir. :slight_smile: Today’s was just Range and MapSet doing everything. I think today’s is probably the closest to literally just restating the problem description in Elixir. Part two required no edits to the code to solve part one since it just required a different count function. Link to Livebook notebook.

defmodule Day4 do
  @moduledoc """
  Solutions for Day 4
  """

  @typedoc """
  Represents a pairing of two elf's section assignments. The section assignments
  are represented by Elixir `Range`s.
  """
  @type assignment_pair() :: {Range.t(), Range.t()}

  @doc """
  Parse an assignment pair string into a tuple of section assignment ranges

  ## Examples:
    iex> Day4.parse_assignment_pair("2-4,6-8")
    {2..4, 6..8}
  """
  @spec parse_assignment_pair(String.t()) :: assignment_pair()
  def parse_assignment_pair(string) do
    [[a_start, a_end], [b_start, b_end]] =
      for range <- String.split(string, ",", trim: true) do
        String.split(range, "-", trim: true)
        |> Enum.map(&String.to_integer/1)
      end

    {Range.new(a_start, a_end), Range.new(b_start, b_end)}
  end

  @doc """
  List of all the assignment pairs
  """
  @spec assignment_pairs() :: [assignment_pair()]
  def assignment_pairs() do
    Utilities.read_data(4)
    |> Enum.map(&parse_assignment_pair/1)
  end

  @doc """
  Determines if range 1 is a subset of range 2
  """
  @spec range_subset?(Range.t(), Range.t()) :: boolean()
  def range_subset?(range1, range2) do
    MapSet.subset?(MapSet.new(range1), MapSet.new(range2))
  end

  @doc """
  Determines if one of the ranges is fully contained in (i.e., a subset of) the other
  """
  @spec range_contained_in_the_other?(Range.t(), Range.t()) :: boolean()
  def range_contained_in_the_other?(range1, range2) do
    range_subset?(range1, range2) or range_subset?(range2, range1)
  end

  def part_one() do
    assignment_pairs()
    |> Enum.count(fn {range1, range2} -> range_contained_in_the_other?(range1, range2) end)
  end

  def part_two() do
    assignment_pairs()
    |> Enum.count(fn {range1, range2} -> !Range.disjoint?(range1, range2) end)
  end
end