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

Day 1: Trebuchet?!

2023/elixir/day-01.livemd

Day 1: Trebuchet?!

Mix.install([
  :kino
])

Input

input = Kino.Input.textarea("Input")

Part 1

defmodule Part1 do
  def calibration_value(line) when is_bitstring(line) do
    line = String.graphemes(line)
    find_integer(line) * 10 + find_integer(Enum.reverse(line))
  end

  def calibration_value(document) when is_list(document) do
    document
    |> Enum.map(fn line -> calibration_value(line) end)
    |> Enum.sum()
  end

  defp find_integer(list) do
    Enum.find_value(list, fn char ->
      case Integer.parse(char) do
        {v, _} -> v
        :error -> false
      end
    end)
  end
end
Kino.Input.read(input)
|> String.split("\n")
|> Part1.calibration_value()

Part 2

defmodule Part2 do
  @digits %{
    "one" => 1,
    "two" => 2,
    "three" => 3,
    "four" => 4,
    "five" => 5,
    "six" => 6,
    "seven" => 7,
    "eight" => 8,
    "nine" => 9,
    "1" => 1,
    "2" => 2,
    "3" => 3,
    "4" => 4,
    "5" => 5,
    "6" => 6,
    "7" => 7,
    "8" => 8,
    "9" => 9
  }

  @digits_reverse for {key, value} <- @digits, into: %{}, do: {String.reverse(key), value}

  def calibration_value(line) when is_bitstring(line) do
    left = find_digit(line, @digits)
    right = find_digit(String.reverse(line), @digits_reverse)
    left * 10 + right
  end

  def calibration_value(document) when is_list(document) do
    document
    |> Enum.map(fn line -> calibration_value(line) end)
    |> Enum.sum()
  end

  defp find_digit(line, digits) do
    digits
    |> Map.keys()
    |> Enum.join("|")
    |> Regex.compile!()
    |> Regex.scan(line)
    |> List.first()
    |> List.first()
    |> then(fn k -> digits[k] end)
  end
end
Kino.Input.read(input)
|> String.split("\n")
|> Part2.calibration_value()

Part 2 - Solution 2

defmodule Part2Solution2 do
  @digits %{
    "one" => 1,
    "two" => 2,
    "three" => 3,
    "four" => 4,
    "five" => 5,
    "six" => 6,
    "seven" => 7,
    "eight" => 8,
    "nine" => 9,
    "1" => 1,
    "2" => 2,
    "3" => 3,
    "4" => 4,
    "5" => 5,
    "6" => 6,
    "7" => 7,
    "8" => 8,
    "9" => 9
  }

  def calibration_value(line) when is_bitstring(line) do
    digits =
      @digits
      |> Map.keys()
      |> Enum.join("|")
      # lookahead assertion
      |> then(fn s -> "(?=(#{s}))" end)
      |> Regex.compile!()
      |> Regex.scan(line)

    [_, left] = List.first(digits)
    [_, right] = List.last(digits)
    @digits[left] * 10 + @digits[right]
  end

  def calibration_value(document) when is_list(document) do
    document
    |> Enum.map(fn line -> calibration_value(line) end)
    |> Enum.sum()
  end
end
Kino.Input.read(input)
|> String.split("\n")
|> Part2Solution2.calibration_value()