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

AOC 2022 - Day 03

aoc2022/day03.livemd

AOC 2022 - Day 03

Mix.install([
  {:kino_aoc, "~> 0.1"}
])

AOC Helper

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2022", "3", System.fetch_env!("LB_AOC_SESSION"))

Part 1

Code

defmodule PartOne do
  def find_shared_item({compartment1, compartment2}) do
    MapSet.intersection(
      MapSet.new(compartment1),
      MapSet.new(compartment2)
    )
    |> MapSet.to_list()
    |> List.first()
  end

  # convert character to ascii code
  def priority(item) when is_bitstring(item), do: :binary.first(item) |> priority()

  # ascii code for 'A' is 65 and 'Z' is 90.  The offset for the puzzles values is 38
  def priority(item) when item >= 65 and item <= 90, do: item - 38

  # ascii code for 'a' is 97 and 'z' is 122.  The offset for the puzzles values is 96
  def priority(item) when item >= 97 and item <= 122, do: item - 96

  def solve(input) do
    IO.puts("--- Part One ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input
    |> String.split("\n")
    |> Enum.reduce(0, fn rucksack, total ->
      contents = rucksack |> String.trim() |> String.graphemes()
      midpoint = contents |> length() |> Kernel./(2) |> round()

      contents
      |> Enum.split(midpoint)
      |> find_shared_item()
      |> priority()
      |> Kernel.+(total)
    end)
  end
end

Test

ExUnit.start(autorun: false)

defmodule PartOneTest do
  use ExUnit.Case, async: true
  import PartOne

  @input "vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw"
  @expected 157

  test "part one" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution

PartOne.solve(puzzle_input)

Part 2

Code

defmodule PartTwo do
  def find_badge([rucksack1, rucksack2, rucksack3]) do
    rucksack1
    |> MapSet.new()
    |> MapSet.intersection(MapSet.new(rucksack2))
    |> MapSet.intersection(MapSet.new(rucksack3))
    |> MapSet.to_list()
    |> List.first()
  end

  # convert character to ascii code
  def priority(item) when is_bitstring(item), do: :binary.first(item) |> priority()

  # ascii code for 'A' is 65 and 'Z' is 90.  The offset for the puzzles values is 38
  def priority(item) when item >= 65 and item <= 90, do: item - 38

  # ascii code for 'a' is 97 and 'z' is 122.  The offset for the puzzles values is 96
  def priority(item) when item >= 97 and item <= 122, do: item - 96

  def solve(input) do
    IO.puts("--- Part Two ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input
    |> String.split("\n")
    |> Enum.chunk_every(3)
    |> Enum.map(fn group ->
      Enum.map(group, &amp;(&amp;1 |> String.trim() |> String.graphemes()))
    end)
    |> Enum.reduce(0, fn rucksacks, total ->
      rucksacks
      |> find_badge()
      |> priority()
      |> Kernel.+(total)
    end)
  end
end

Test

ExUnit.start(autorun: false)

defmodule PartTwoTest do
  use ExUnit.Case, async: true
  import PartTwo

  @input "vJrwpWtwJgWrhcsFMMfFFhFp
jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL
PmmdzqPrVvPwwTWBwg
wMqvLMZHhHMvwLHjbvcjnnSBnvTQFn
ttgJtRGJQctTZtZT
CrZsJsPPZsGzwwsLwLmpwMDw"
  @expected 70

  test "part two" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution

PartTwo.solve(puzzle_input)