139 lines
2.7 KiB
Elixir
139 lines
2.7 KiB
Elixir
defmodule Day1_2024 do
|
|
@doc """
|
|
Solves Day 1
|
|
|
|
## Examples
|
|
|
|
iex> ~s(1abc2
|
|
iex>qr3stu8vwx
|
|
iex>1b2c3d4e5f
|
|
iex>reb7uchet) |> String.split(~s(
|
|
iex>)) |> Day1_2024.solve()
|
|
142
|
|
"""
|
|
def solve(input) do
|
|
input
|
|
|> Enum.filter(&(String.length(&1) > 0))
|
|
|> Enum.reduce(
|
|
0,
|
|
fn line, acc ->
|
|
acc + parse_number(line)
|
|
end
|
|
)
|
|
end
|
|
|
|
def parse_number(line) do
|
|
left_number = find_first_number(line)
|
|
right_number = find_first_number_from_right(line)
|
|
left_number * 10 + right_number
|
|
end
|
|
|
|
def find_first_number(l) do
|
|
1..(l |> String.length())
|
|
|> Enum.reduce_while(
|
|
0,
|
|
fn i, _ ->
|
|
int_at_pos = l |> String.at(i - 1) |> Integer.parse()
|
|
|
|
case int_at_pos do
|
|
:error ->
|
|
l
|
|
|> String.slice(0, i)
|
|
|> search_for_written_number()
|
|
|> then(fn res ->
|
|
case res do
|
|
nil -> {:cont, 0}
|
|
n -> {:halt, n}
|
|
end
|
|
end)
|
|
|
|
n ->
|
|
{:halt, n |> elem(0)}
|
|
end
|
|
end
|
|
)
|
|
end
|
|
|
|
def find_first_number_from_right(l) do
|
|
len = String.length(l)
|
|
|
|
1..len
|
|
|> Enum.reduce_while(
|
|
0,
|
|
fn i, _ ->
|
|
cur_char = l |> String.at(len - i)
|
|
int_at_pos = cur_char |> Integer.parse()
|
|
|
|
case int_at_pos do
|
|
:error ->
|
|
l
|
|
|> String.slice(len - i, len)
|
|
|> search_for_written_number()
|
|
|> then(fn res ->
|
|
case res do
|
|
nil -> {:cont, 0}
|
|
n -> {:halt, n}
|
|
end
|
|
end)
|
|
|
|
{n, _} ->
|
|
{:halt, n}
|
|
end
|
|
end
|
|
)
|
|
end
|
|
|
|
def search_for_written_number(line) do
|
|
numbers = [
|
|
"one",
|
|
"two",
|
|
"three",
|
|
"four",
|
|
"five",
|
|
"six",
|
|
"seven",
|
|
"eight",
|
|
"nine"
|
|
]
|
|
|
|
numbers_map = %{
|
|
"one" => 1,
|
|
"two" => 2,
|
|
"three" => 3,
|
|
"four" => 4,
|
|
"five" => 5,
|
|
"six" => 6,
|
|
"seven" => 7,
|
|
"eight" => 8,
|
|
"nine" => 9
|
|
}
|
|
|
|
if String.length(line) < 3 do
|
|
nil
|
|
else
|
|
splits =
|
|
numbers
|
|
|> Enum.map(fn number ->
|
|
split = String.split(line, number)
|
|
{number, split |> Enum.map(&(&1 |> String.length()))}
|
|
end)
|
|
|> Enum.filter(&(&1 |> elem(1) |> Enum.count() > 1))
|
|
|
|
number_word =
|
|
splits
|
|
|> Enum.sort_by(fn split ->
|
|
split |> elem(1) |> List.first()
|
|
end)
|
|
|> Enum.at(0)
|
|
|
|
res =
|
|
if is_tuple(number_word) do
|
|
Map.get(numbers_map, number_word |> elem(0))
|
|
else
|
|
nil
|
|
end
|
|
|
|
res
|
|
end
|
|
end
|
|
end
|