Advent 2023 - Day 3
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 row ->
row
|> String.codepoints()
|> Enum.map(fn cell ->
case Integer.parse(cell) do
{x, _} -> x
:error -> cell
end
end)
|> Enum.map(fn cell ->
case cell do
"." -> nil
x -> x
end
end)
end)
[
[4, 6, 7, nil, nil, 1, 1, 4, nil, nil],
[nil, nil, nil, "*", nil, nil, nil, nil, nil, nil],
[nil, nil, 3, 5, nil, nil, 6, 3, 3, nil],
[nil, nil, nil, nil, nil, nil, "#", nil, nil, nil],
[6, 1, 7, "*", nil, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, "+", nil, 5, 8, nil],
[nil, nil, 5, 9, 2, nil, nil, nil, nil, nil],
[nil, nil, nil, nil, nil, nil, 7, 5, 5, nil],
[nil, nil, nil, "$", nil, "*", nil, nil, nil, nil],
[nil, 6, 6, 4, nil, 5, 9, 8, nil, nil]
]
Part 1
defmodule Schematic do
def surronding(grid, {x, y}) do
for xp <- -1..1 do
for yp <- -1..1 do
x = xp + x
y = yp + y
if (xp == 0 && yp == 0) || x < 0 || y < 0 do
{nil, {x, y}}
else
row = Enum.at(grid, y)
if is_nil(row) do
{nil, {x, y}}
else
{Enum.at(row, x), {x, y}}
end
end
end
end
end
def mark_seen(grid) do
grid
|> Enum.with_index()
|> Enum.map(fn {row, y} ->
row
|> Enum.with_index()
|> Enum.map(fn {cell, x} ->
near_symbol =
surronding(grid, {x, y})
|> List.flatten()
|> Enum.map(&elem(&1, 0))
|> Enum.filter(&is_bitstring(&1))
|> Enum.any?()
{cell, near_symbol}
end)
end)
end
def smush_into_part_ids(row) do
smush_into_part_ids(row, [], [])
|> Enum.map(&Enum.reverse(&1))
end
def smush_into_part_ids([{num, symbol} | rest], cur_num, so_far) when is_number(num) do
smush_into_part_ids(rest, [{num, symbol} | cur_num], so_far)
end
def smush_into_part_ids([_not_num | rest], cur_num, so_far) when length(cur_num) == 0 do
smush_into_part_ids(rest, cur_num, so_far)
end
def smush_into_part_ids([_not_num | rest], cur_num, so_far) do
smush_into_part_ids(rest, [], [cur_num | so_far])
end
def smush_into_part_ids([], cur_num, so_far) when length(cur_num) == 0 do
so_far
end
def smush_into_part_ids([], cur_num, so_far) do
[cur_num | so_far]
end
end
{:module, Schematic, <<70, 79, 82, 49, 0, 0, 18, ...>>, {:smush_into_part_ids, 3}}
Schematic.mark_seen(input)
|> Enum.map(&Schematic.smush_into_part_ids(&1))
|> Enum.concat()
|> Enum.filter(fn part_id ->
Enum.any?(part_id, fn {_, symbol} -> symbol end)
end)
|> Enum.map(fn part_id ->
part_id
|> Enum.map(&elem(&1, 0))
|> Enum.join("")
|> Integer.parse()
|> elem(0)
end)
|> Enum.sum()
4361
Part 2
defmodule GearSchematic do
def mark_seen(grid) do
grid
|> Enum.with_index()
|> Enum.map(fn {row, y} ->
row
|> Enum.with_index()
|> Enum.map(fn {cell, x} ->
near_symbols =
Schematic.surronding(grid, {x, y})
|> List.flatten()
|> Enum.filter(fn {val, _} -> val == "*" end)
has_near_symbols = Enum.any?(near_symbols)
{cell, {has_near_symbols, near_symbols}}
end)
end)
end
end
{:module, GearSchematic, <<70, 79, 82, 49, 0, 0, 10, ...>>, {:mark_seen, 1}}
parts =
GearSchematic.mark_seen(input)
|> Enum.map(&Schematic.smush_into_part_ids(&1))
|> Enum.concat()
|> Enum.filter(fn part_id ->
Enum.any?(part_id, fn {_, {symbol, _}} -> symbol end)
end)
|> Enum.map(fn part_id ->
number =
part_id
|> Enum.map(&elem(&1, 0))
|> Enum.join("")
|> Integer.parse()
|> elem(0)
matched_symbols =
part_id
|> Enum.filter(fn {_, {seen, _}} -> seen end)
|> Enum.map(fn {_, {_, positions}} ->
positions
|> Enum.map(fn {_, pos} -> pos end)
end)
|> List.flatten()
|> Enum.uniq()
{number, matched_symbols}
end)
[{467, [{3, 1}]}, {35, [{3, 1}]}, {617, [{3, 4}]}, {755, [{5, 8}]}, {598, [{5, 8}]}]
Enum.reduce(parts, %{}, fn {part_number, neighbors}, acc ->
Enum.reduce(neighbors, acc, fn point, acc ->
Map.get_and_update(acc, point, fn cur ->
next =
if is_nil(cur) do
[part_number]
else
[part_number | cur]
end
{cur, next}
end)
|> elem(1)
end)
end)
|> Map.filter(fn {_key, val} -> length(val) == 2 end)
|> Map.values()
|> Enum.map(fn [r, l] -> r * l end)
|> Enum.sum()
467835