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

Day 1

2021/day_03.livemd

Day 1

Common

defmodule Day3 do
  def parse_input(raw_input) do
    raw_input
    |> String.split("\n", trim: true)
    |> Enum.map(fn line ->
      line
      |> String.split("", trim: true)
      |> Enum.map(&String.to_integer/1)
    end)
  end

  def counts(input) do
    input
    |> Enum.flat_map(fn digits ->
      Enum.with_index(digits)
    end)
    |> Enum.group_by(&elem(&1, 1))
    |> Enum.map(fn {_index, tuples} ->
      {
        Enum.count(tuples, &(elem(&1, 0) == 0)),
        Enum.count(tuples, &(elem(&1, 0) == 1))
      }
    end)
  end

  def most_common(counts) do
    Enum.map(counts, fn
      {zeros, ones} when zeros > ones -> 0
      {zeros, ones} when zeros < ones -> 1
      {same, same} -> :equal
    end)
  end

  def least_common(counts) do
    Enum.map(counts, fn
      {zeros, ones} when zeros > ones -> 1
      {zeros, ones} when zeros < ones -> 0
      {same, same} -> :equal
    end)
  end

  def digits_to_decimal(digits) do
    digits
    |> Enum.join()
    |> String.to_integer(2)
  end
end

Input

Mix.install([{:kino, github: "livebook-dev/kino"}])

textarea = Kino.Input.textarea("Input:")
raw_input = Kino.Input.read(textarea)
input = Day3.parse_input(raw_input)

Part 1

defmodule Day3.Part1 do
  def run(input) do
    gamma =
      input
      |> Day3.counts()
      |> Day3.most_common()
      |> Day3.digits_to_decimal()

    epsilon =
      input
      |> Day3.counts()
      |> Day3.least_common()
      |> Day3.digits_to_decimal()

    gamma * epsilon
  end
end
Day3.Part1.run(input)

Part 2

defmodule Day3.Part2 do
  def run(input) do
    oxygen_generator_rating = oxygen_generator_rating(input)

    co2_scrubber_rating = co2_scrubber_rating(input)

    oxygen_generator_rating * co2_scrubber_rating
  end

  def oxygen_generator_rating(input) do
    reduce_by(input, &amp;Day3.most_common/1, 1)
  end

  def co2_scrubber_rating(input) do
    reduce_by(input, &amp;Day3.least_common/1, 0)
  end

  #
  # private
  # 

  defp reduce_by(input, common_fun, digit_if_equal) do
    digits_count =
      input
      |> hd()
      |> Enum.count()

    Enum.reduce_while(0..(digits_count - 1), input, fn
      _index, [_digits] = acc ->
        {:halt, acc}

      index, acc ->
        counts1 = Day3.counts(acc)
        common1 = common_fun.(counts1)

        common_digit =
          case Enum.at(common1, index) do
            :equal -> digit_if_equal
            digit -> digit
          end

        {:cont,
         Enum.filter(acc, fn digits ->
           Enum.at(digits, index) == common_digit
         end)}
    end)
    |> hd()
    |> Day3.digits_to_decimal()
  end
end
Day3.Part2.run(input)