Skip to content

Latest commit

 

History

History
159 lines (118 loc) · 3.85 KB

day03.livemd

File metadata and controls

159 lines (118 loc) · 3.85 KB

Advent of Code 2021 - Day 3

Puzzle description

Binary Diagnostic

The diagnostic report of the submarine

List of binary numbers which, when decoded properly, can tell you many useful things about the conditions of the submarine.

defmodule Load do
  def input do
    File.read!("inputs/day03.txt")
    |> String.split("\n", trim: true)
  end
end

Check the power consumption

You need to use the binary numbers in the diagnostic report to generate two new binary numbers (called the gamma rate and the epsilon rate).

Generating the Gamma rate

Each bit in the gamma rate can be determined by finding the most common bit in the corresponding position of all numbers in the diagnostic report.

defmodule Part1_gamma do
  def gamma_rate() do
    input = Load.input()
    line_length = String.length(hd(input))
    most_common_bit(input, line_length, 0, [])
  end

  defp most_common_bit(_data, position, position, result) do
    result |> Enum.join() |> String.to_integer(2)
  end

  defp most_common_bit(data, length, position, result) do
    bits =
      Enum.map(data, fn line -> String.at(line, position) end)
      |> Enum.frequencies()

    {bit, _} = Enum.max_by(bits, fn {_bit, count} -> count end)
    most_common_bit(data, length, position + 1, result ++ [bit])
  end
end

Generating the Epsilon rate

The least common bit from each position is used.

defmodule Part1_epsilon do
  def epsilon_rate do
    input = Load.input()
    line_length = String.length(hd(input))
    least_common_bit(input, line_length, 0, [])
  end

  defp least_common_bit(_data, position, position, result) do
    result |> Enum.join() |> String.to_integer(2)
  end

  defp least_common_bit(data, length, position, result) do
    bits =
      Enum.map(data, fn line -> String.at(line, position) end)
      |> Enum.frequencies()

    {bit, _} = Enum.min_by(bits, fn {_bit, count} -> count end)
    least_common_bit(data, length, position + 1, result ++ [bit])
  end
end

The power consumption

Multiplying the gamma rate by the epsilon rate.

defmodule Part1_power_consumption do
  def check do
    Part1_gamma.gamma_rate() * Part1_epsilon.epsilon_rate()
  end
end

Part1_power_consumption.check()

Verify the life support rating

Life support rating can be determined by multiplying the oxygen generator rating by the CO2 scrubber rating.

defmodule Part2 do
  def life_support_rating() do
    input = Load.input() |> Enum.map(&String.codepoints/1)

    find_rating(input, &oxygen_generator_criteria/1, "1", 0) *
      find_rating(input, &co2_scrubber_criteria/1, "0", 0)
  end

  defp oxygen_generator_criteria(values) do
    {bit, _} = Enum.max_by(values, fn {_bit, count} -> count end)
    bit
  end

  defp co2_scrubber_criteria(values) do
    {bit, _} = Enum.min_by(values, fn {_bit, count} -> count end)
    bit
  end

  defp find_rating([result], _criteria, _default_value, _position) do
    result |> Enum.join() |> String.to_integer(2)
  end

  defp find_rating(data, criteria, default_value, position) do
    values =
      data
      |> Enum.map(fn line -> Enum.at(line, position) end)
      |> Enum.frequencies()

    common =
      cond do
        values["0"] == values["1"] -> default_value
        true -> criteria.(values)
      end

    new_data = Enum.filter(data, fn line -> Enum.at(line, position) == common end)
    find_rating(new_data, criteria, default_value, position + 1)
  end
end

Part2.life_support_rating()

Check that my answers are still correct after refactoring

ExUnit.start(autorun: false)

defmodule Test do
  use ExUnit.Case, async: true

  test "part1" do
    assert Part1_power_consumption.check() == 738_234
  end

  test "part2" do
    assert Part2.life_support_rating() == 3_969_126
  end
end

ExUnit.run()