Powered by AppSignal & Oban Pro

Day 15

notebooks/day15.livemd

Day 15

Mix.install([
  {:req, "~> 0.4.5"},
  {:kino, "~> 0.11.3"}
])

Input

input =
  Req.get!(
    "https://adventofcode.com/2023/day/15/input",
    headers: [{"Cookie", ~s"session=#{System.fetch_env!("LB_AOC_SESSION")}"}]
  ).body

Part 1

defmodule Part1 do
  def parse(input) do
    input
    |> String.trim_trailing("\n")
    |> String.split(",")
  end

  def hash(str) do
    for code <- String.to_charlist(str), reduce: 0 do
      acc -> rem(17 * (acc + code), 256)
    end
  end
end

Part1.parse(input)
|> Enum.map(&amp;Part1.hash/1)
|> Enum.sum()

Part 2

defmodule Part2 do
  def initialize(strs) do
    for str <- strs, reduce: %{} do
      boxes -> step(str, boxes)
    end
  end

  def step(str, boxes) do
    case Regex.run(~r/(\w+)([-=])(\d+)?/, str, capture: :all_but_first) do
      [label, "-"] ->
        key = Part1.hash(label)

        Map.update(boxes, key, [], fn box ->
          index =
            Enum.find_index(box, fn {other, _} ->
              other == label
            end)

          if index, do: List.delete_at(box, index), else: box
        end)

      [label, "=", flen] ->
        key = Part1.hash(label)
        lens = {label, flen}

        Map.update(boxes, key, [lens], fn box ->
          index =
            Enum.find_index(box, fn {other, _} ->
              other == label
            end)

          if index do
            List.replace_at(box, index, lens)
          else
            box ++ [lens]
          end
        end)
    end
  end

  def power(boxes) do
    for {box, lenses} <- boxes,
        {{_, flen}, slot} <- Enum.with_index(lenses, 1),
        reduce: 0 do
      acc -> acc + (1 + box) * String.to_integer(flen) * slot
    end
  end
end

Part1.parse(input)
|> Part2.initialize()
|> Part2.power()