Day 3
Mix.install([
{:kino, "~> 0.11.3"}
])
Input
input = Kino.Input.textarea("Input")
Part 1
example = """
467..114..
...*......
..35..633.
......#...
617*......
.....+.58.
..592.....
......755.
...$.*....
.664.598..
"""
expected = 4361
4361
defmodule Part1 do
def parse(input) do
for {line, row} <- String.split(input, "\n", trim: true) |> Enum.with_index(), reduce: %{} do
acc ->
for [{col, len}] <- Regex.scan(~r/\d+|[^.]/, line, return: :index), into: acc do
string = String.slice(line, col, len)
case Integer.parse(string) do
:error -> {{row, col}, string}
{int, ""} -> {{row, col}, int}
end
end
end
end
def classify({_, value}) when is_integer(value), do: :number
def classify({_, _}), do: :symbol
def adjacent?({{row, col}, integer}, check) do
digits = Integer.digits(integer) |> Enum.count()
top_bottom = Enum.map((col - 1)..(col + digits), &[{row - 1, &1}, {row + 1, &1}])
left_right = [{row, col - 1}, {row, col + digits}]
List.flatten([left_right, top_bottom])
|> Enum.any?(&Enum.member?(check, &1))
end
def run(input) do
%{symbol: symbols, number: numbers} = parse(input) |> Enum.group_by(&classify/1)
symbol_set = MapSet.new(symbols, &elem(&1, 0))
Enum.filter(numbers, &adjacent?(&1, symbol_set))
|> Enum.map(&elem(&1, 1))
|> Enum.sum()
end
end
Part1.run(example) == expected
true
Kino.Input.read(input)
|> Part1.run()
529618
Part 2
expected = [16345, 451_490]
[16345, 451490]
defmodule Part2 do
def classify({_, "*"}), do: :maybe_gear
def classify({_, value}) when is_integer(value), do: :number
def classify({_, _}), do: :symbol
def run(input) do
%{maybe_gear: maybe_gears, number: numbers} =
Part1.parse(input) |> Enum.group_by(&classify/1)
gear_parts =
for gear <- Enum.map(maybe_gears, &elem(&1, 0)) do
Enum.filter(numbers, &Part1.adjacent?(&1, [gear]))
|> Enum.map(&elem(&1, 1))
end
Enum.filter(gear_parts, &(Enum.count(&1) == 2))
|> Enum.map(&Enum.product/1)
end
end
Part2.run(example) == expected
true
Kino.Input.read(input)
|> Part2.run()
|> Enum.sum()
77509019