Powered by AppSignal & Oban Pro

aoc2021-day3

2021/elixir/day-3.livemd

aoc2021-day3

Part 1

Mix.install([{:kino, "~> 0.4.1"}])
input = Kino.Input.textarea("inputs")
gamma_list =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&String.codepoints/1)
  |> Enum.zip_with(fn list -> Enum.frequencies(list) |> Enum.max_by(&elem(&1, 1)) |> elem(0) end)

gamma = gamma_list |> Enum.join() |> Integer.parse(2) |> elem(0)

epsilon =
  gamma_list
  |> Enum.map_join(fn
    "1" -> "0"
    "0" -> "1"
  end)
  |> Integer.parse(2)
  |> elem(0)

gamma * epsilon

Part 2

input_list =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&String.codepoints/1)

win_bit = input_list |> Enum.map(&Enum.at(&1, 0)) |> Enum.frequencies()
defmodule Part2 do
  def solve(input_list) do
    max_fn = fn
      fq_map ->
        if (fq_map["0"] || 0) > (fq_map["1"] || 0), do: "0", else: "1"
    end

    min_fn = fn
      fq_map ->
        if (fq_map["0"] || 0) <= (fq_map["1"] || 0), do: "0", else: "1"
    end

    oxygen = filter_one_by_position(input_list, 0, max_fn) |> bit_to_integer()

    co2 = filter_one_by_position(input_list, 0, min_fn) |> bit_to_integer()

    oxygen * co2
  end

  def filter_one_by_position(input_list, position, pick_fn)
  def filter_one_by_position([last_one], _position, _pick_fn), do: last_one

  def filter_one_by_position(input_list, position, pick_fn) when length(input_list) > 1 do
    win_bit = input_list |> Enum.map(&Enum.at(&1, position)) |> Enum.frequencies() |> pick_fn.()

    input_list
    |> Enum.filter(fn bits -> Enum.at(bits, position) == win_bit end)
    |> filter_one_by_position(position + 1, pick_fn)
  end

  defp bit_to_integer(bits), do: bits |> Enum.join() |> Integer.parse(2) |> elem(0)
end
Part2.solve(input_list)

Jose’s Solution

defmodule Recursion do
  defp recur([number], _pos, _fun) do
    number
    |> Tuple.to_list()
    |> List.to_integer(2)
  end

  defp recur(numbers, pos, fun) do
    zero_count = Enum.count(numbers, &(elem(&1, pos) == ?0))
    one_count = length(numbers) - zero_count
    to_keep = fun.(zero_count, one_count)
    numbers = Enum.filter(numbers, &(elem(&1, pos) == to_keep))
    recur(numbers, pos + 1, fun)
  end

  def o2(numbers) do
    recur(numbers, 0, fn zero_count, one_count ->
      if one_count >= zero_count, do: ?1, else: ?0
    end)
  end

  def co2(numbers) do
    recur(numbers, 0, fn zero_count, one_count ->
      if zero_count <= one_count, do: ?0, else: ?1
    end)
  end
end

numbers =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
  |> Enum.map(&(&1 |> String.to_charlist() |> List.to_tuple()))

Recursion.o2(numbers) * Recursion.co2(numbers)