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

Day 3: Gear Ratios

2023/day_03.livemd

Day 3: Gear Ratios

Mix.install([:kino])

input = Kino.Input.textarea("Please paste your input:")

Part 1

Run in Livebook

https://adventofcode.com/2023/day/3

data =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
results =
  data
  |> Enum.with_index()
  |> Enum.map(fn {line, y} ->
    numbers_symbols =
      (line <> ".")
      |> String.codepoints()
      |> Enum.with_index()
      |> Enum.reduce(
        %{numbers: [], symbols: []},
        fn {c, x}, acc ->
          case Integer.parse(c) do
            {number, _} ->
              # number
              acc
              |> Map.update(
                :number_temp,
                {_number_acc = number, _number_start_x = x},
                fn {number_acc, number_start_x} ->
                  {number_acc * 10 + number, number_start_x}
                end
              )

            :error ->
              # symbol or . (number is end)
              acc =
                case acc[:number_temp] do
                  {number_acc, number_start_x} ->
                    acc
                    |> Map.update!(
                      :numbers,
                      &amp;[{number_acc, {(number_start_x - 1)..x, (y - 1)..(y + 1)}} | &amp;1]
                    )
                    |> Map.delete(:number_temp)

                  _ ->
                    acc
                end

              case c do
                # .
                "." ->
                  acc

                # symbol
                symbol_str ->
                  acc |> Map.update!(:symbols, &amp;[{symbol_str, {x, y}} | &amp;1])
              end
          end
        end
      )

    {y, numbers_symbols}
  end)
  |> Map.new()

results
|> Enum.map(fn {_y, %{numbers: numbers}} ->
  numbers
  |> Enum.filter(fn {_number, {x_range, y_range}} ->
    y_range
    |> Enum.any?(fn y ->
      (get_in(results, [y, :symbols]) || [])
      |> Enum.any?(fn {_symbol_str, {x, _y}} -> x in x_range end)
    end)
  end)
  |> Enum.map(fn {number, _position} -> number end)
end)
|> List.flatten()
|> Enum.sum()

Part 2

https://adventofcode.com/2023/day/3#part2

results
|> Enum.map(fn {_y, %{symbols: symbols}} ->
  symbols
  |> Enum.filter(fn {symbol_str, _position} -> symbol_str == "*" end)
  |> Enum.map(fn {_symbol_str, {x, y}} ->
    (y - 1)..(y + 1)
    |> Enum.flat_map(fn y ->
      (get_in(results, [y, :numbers]) || [])
      |> Enum.filter(fn {_number, {x_range, _y_range}} ->
        x in x_range
      end)
      |> Enum.map(fn {number, _position} -> number end)
    end)
    |> List.flatten()
    |> case do
      [part1, part2] -> part1 * part2
      _ -> 0
    end
  end)
end)
|> List.flatten()
|> Enum.sum()