Powered by AppSignal & Oban Pro

Day 03

elixir/day03.livemd

Day 03

Setup

Mix.install([
  {:kino, "~> 0.4.1"}
])
input = Kino.Input.textarea("Problem input")

Part 1

gamma_rate =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Stream.map(&String.codepoints/1)
  |> Enum.zip()
  |> Enum.map(
    &(Tuple.to_list(&1)
      |> Enum.frequencies()
      |> Enum.max_by(fn {_b, count} -> count end))
  )
  |> Enum.map(fn {b, _c} -> b end)
  |> Enum.join()

epsilon_rate =
  gamma_rate
  |> String.codepoints()
  |> Stream.map(fn
    "1" -> "0"
    "0" -> "1"
  end)
  |> Enum.join()

String.to_integer(gamma_rate, 2) * String.to_integer(epsilon_rate, 2)

Part 2

defmodule Solver do
  def bit_frequencies(numbers_list, position) do
    numbers_list
    |> Stream.map(&String.at(&1, position))
    |> Enum.frequencies()
  end

  def o2_bit_criteria(numbers_list, position) do
    bit_frequencies(numbers_list, position)
    |> then(fn
      %{"0" => zeroes, "1" => ones} when ones >= zeroes -> "1"
      _ -> "0"
    end)
  end

  def co2_bit_criteria(numbers_list, position) do
    bit_frequencies(numbers_list, position)
    |> then(fn
      %{"0" => zeroes, "1" => ones} when ones < zeroes -> "1"
      _ -> "0"
    end)
  end

  def find_rating(numbers) do
    o2_rating = find_rating(numbers, 0, &amp;o2_bit_criteria/2)
    co2_rating = find_rating(numbers, 0, &amp;co2_bit_criteria/2)
    String.to_integer(o2_rating, 2) * String.to_integer(co2_rating, 2)
  end

  def find_rating(numbers, _position, _criteria_fun) when length(numbers) == 1,
    do: List.first(numbers)

  def find_rating(numbers, position, criteria_fun) do
    criteria = criteria_fun.(numbers, position)

    find_rating(
      Enum.filter(numbers, &amp;(String.at(&amp;1, position) == criteria)),
      position + 1,
      criteria_fun
    )
  end
end

input
|> Kino.Input.read()
|> String.split("\n")
|> Solver.find_rating()