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

Day 1: Trebuchet?!

2023/day_01.livemd

Day 1: Trebuchet?!

Mix.install([:kino])

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

Part 1

Run in Livebook

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

data =
  input
  |> Kino.Input.read()
  |> String.split("\n", trim: true)
data
|> Enum.map(fn calibration ->
  only_number_string = Regex.replace(~r/[^\d]/, calibration, "")

  "#{only_number_string |> String.at(0)}#{only_number_string |> String.at(-1)}"
  |> String.to_integer()
end)
|> Enum.sum()

Part 2

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

defmodule Day01.Part2.Solve1 do
  @number_words ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
  @number_strings 1..9 |> Enum.map(&Integer.to_string/1)
  @number_matches (@number_words |> Enum.with_index(1)) ++ (@number_strings |> Enum.with_index(1))

  def recover(calibration) do
    do_recover(calibration, [])
    |> Enum.reverse()
    |> then(&("#{List.first(&1)}#{List.last(&1)}" |> String.to_integer()))
  end

  defp do_recover("", result) do
    result
  end

  defp do_recover(string, result) do
    new_result =
      @number_matches
      |> Enum.find_value(fn {number_match, number} ->
        if string |> String.starts_with?(number_match) do
          number
        end
      end)
      |> case do
        nil -> result
        number -> [number | result]
      end

    do_recover(string |> String.slice(1..-1), new_result)
  end
end

data
|> Enum.map(fn calibration ->
  Day01.Part2.Solve1.recover(calibration)
end)
|> Enum.sum()
defmodule Day01.Part2.Solve2 do
  @number_words ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
  @number_strings 1..9 |> Enum.map(&Integer.to_string/1)
  @number_matches (@number_words |> Enum.with_index(1)) ++ (@number_strings |> Enum.with_index(1))

  def recover(calibration) do
    do_recover(calibration, [])
    |> then(&("#{List.first(&1)}#{List.last(&1)}" |> String.to_integer()))
  end

  defp do_recover("", result) do
    result
  end

  defp do_recover(string, result) do
    new_result =
      @number_matches
      |> Enum.find_value(fn {number_match, number} ->
        if string |> String.starts_with?(number_match) do
          number
        end
      end)
      |> case do
        nil -> result
        number -> (result |> Enum.slice(0..0)) ++ [number]
      end

    do_recover(string |> String.slice(1..-1), new_result)
  end
end

data
|> Enum.map(fn calibration ->
  Day01.Part2.Solve2.recover(calibration)
end)
|> Enum.sum()
defmodule Day01.Part2.Solve3 do
  @number_words ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]
  @number_strings 1..9 |> Enum.map(&Integer.to_string/1)
  @number_matches (@number_words |> Enum.with_index(1)) ++ (@number_strings |> Enum.with_index(1))

  def recover(calibration) do
    [
      {&String.starts_with?/2, 1..-1},
      {&String.ends_with?/2, 0..-2}
    ]
    |> Enum.map(fn {match_fun, range} ->
      do_recover(calibration, match_fun, range)
    end)
    |> then(fn [first, last] -> "#{first}#{last}" |> String.to_integer() end)
  end

  defp do_recover(string, match_fun, range) do
    @number_matches
    |> Enum.find_value(fn {number_match, number} ->
      if string |> match_fun.(number_match) do
        number
      end
    end)
    |> case do
      nil -> do_recover(string |> String.slice(range), match_fun, range)
      number -> number
    end
  end
end

data
|> Enum.map(fn calibration ->
  Day01.Part2.Solve3.recover(calibration)
end)
|> Enum.sum()