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

Advent 2023 - Day 1

day1.livemd

Advent 2023 - Day 1

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

Section

input = Kino.Input.textarea("Please paste your input file")
input =
  input
  |> Kino.Input.read()
  |> String.split("\n")
  |> Enum.map(fn line ->
    line
    |> String.codepoints()
    |> Enum.map(fn codepoint ->
      case Integer.parse(codepoint) do
        {x, _} -> x
        :error -> codepoint
      end
    end)
  end)
[
  ["t", "w", "o", 1, "n", "i", "n", "e"],
  ["e", "i", "g", "h", "t", "w", "o", "t", "h", "r", "e", "e"],
  ["a", "b", "c", "o", "n", "e", 2, "t", "h", "r", "e", "e", "x", "y", "z"],
  ["x", "t", "w", "o", "n", "e", 3, "f", "o", "u", "r"],
  [4, "n", "i", "n", "e", "e", "i", "g", "h", "t", "s", "e", "v", "e", "n", 2],
  ["z", "o", "n", "e", "i", "g", "h", "t", 2, 3, 4],
  [7, "p", "q", "r", "s", "t", "s", "i", "x", "t", "e", "e", "n"]
]

Part 1

input
|> Enum.map(fn line ->
  first = Enum.find(line, &is_number(&1))
  last = Enum.find(Enum.reverse(line), &is_number(&1))

  if is_nil(first) do
    0
  else
    first * 10 + last
  end
end)
|> Enum.sum()
209

Part 2

number_words = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine"]

number_words_reversed =
  number_words |> Enum.map(&String.reverse(&1))

all_words =
  (number_words ++ number_words_reversed)
  |> Enum.map(&String.codepoints(&1))
[
  ["o", "n", "e"],
  ["t", "w", "o"],
  ["t", "h", "r", "e", "e"],
  ["f", "o", "u", "r"],
  ["f", "i", "v", "e"],
  ["s", "i", "x"],
  ["s", "e", "v", "e", "n"],
  ["e", "i", "g", "h", "t"],
  ["n", "i", "n", "e"],
  ["e", "n", "o"],
  ["o", "w", "t"],
  ["e", "e", "r", "h", "t"],
  ["r", "u", "o", "f"],
  ["e", "v", "i", "f"],
  ["x", "i", "s"],
  ["n", "e", "v", "e", "s"],
  ["t", "h", "g", "i", "e"],
  ["e", "n", "i", "n"]
]
defmodule Trebuchet do
  def calibrate([], _words), do: nil

  def calibrate([num | _rest], _words) when is_number(num) do
    num
  end

  def calibrate(line, words) do
    num =
      words
      |> Enum.find(fn word ->
        List.starts_with?(line, word)
      end)

    if is_nil(num) do
      Trebuchet.calibrate(tl(line), words)
    else
      Trebuchet.understand_handwriting(num, words)
    end
  end

  def understand_handwriting(word, words) do
    index = Enum.find_index(words, fn w -> w == word end)
    value = if index >= 9, do: index - 8, else: index + 1
    value
  end
end
{:module, Trebuchet, <<70, 79, 82, 49, 0, 0, 10, ...>>, {:understand_handwriting, 2}}
input
|> Enum.map(fn line ->
  first = Trebuchet.calibrate(line, all_words)
  last = Trebuchet.calibrate(Enum.reverse(line), all_words)

  first * 10 + last
end)
|> Enum.sum()
281