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

Day 1 - Advent of Code 2023

2023/day1.livemd

Day 1 - Advent of Code 2023

Mix.install([:kino])

Input

example_input = """
two1nine
eightwothree
abcone2threexyz
xtwone3four
4nineeightseven2
zoneight234
7pqrstsixteen
"""

Parsing

# Kino can be used in case this is being run on a elixir's livebook
input = Kino.Input.textarea("Please, insert your input here: ")
defmodule DayOne do
  @numbers %{
    one: 1,
    two: 2,
    three: 3,
    four: 4,
    five: 5,
    six: 6,
    seven: 7,
    eight: 8,
    nine: 9
  }

  def parse_input(input) do
    input
    |> String.split("\n", trim: true)
    |> Enum.map(&String.to_charlist/1)
  end

  @doc """
  Filters characters that match digits between [0..9]
  and transforms it to a list of integer
  """
  def filter_integers(line) do
    numbers = Enum.filter(line, fn char -> char in ?0..?9 end)
    first_number = hd(numbers)

    last_number =
      numbers
      |> List.last()

    [first_number, last_number]
    |> List.to_integer()
  end

  @doc """
  Transforms a number into a parseable digit
  surrounded by the written nubmer to be processed
  by the rest of the solution.

  In order for the solution to work with
  overlapping words, I decided to surround the parsed digit with the word,
  so it would not affect prior or later possible parseable words

  ## Example
  # iex> transform_words("eightwothree")
  # "eight8eightwo2twothree3three"

  """
  def transform_words(input) do
    @numbers
    |> Enum.reduce(input, fn {word, number}, s ->
      String.replace(s, "#{word}", "#{word}#{number}#{word}")
    end)
  end
end

Part 1

input
|> Kino.Input.read()
|> DayOne.parse_input()
|> Enum.map(&DayOne.filter_integers/1)
|> Enum.sum()

Part 2

input
|> Kino.Input.read()
|> DayOne.transform_words()
|> DayOne.parse_input()
|> Enum.map(&DayOne.filter_integers/1)
|> Enum.sum()