AoC 2022 Day 14
Mix.install([:kino])
defmodule Utils do
def split(line, sep \\ "") do
String.split(line, sep, trim: true)
end
def split_all_lines(text, sep \\ "") do
text
|> String.split("\n", trim: true)
|> Enum.map(&split(&1, sep))
end
def to_numbers(number) when is_binary(number) do
String.to_integer(number)
end
def to_numbers(numbers) when is_list(numbers) do
Enum.map(numbers, &to_numbers/1)
end
def to_matrix(text, sep \\ "") do
text
|> split_all_lines(sep)
|> then(fn data ->
for {row, r} <- Enum.with_index(data), {col, c} <- Enum.with_index(row) do
{{r, c}, col}
end
end)
|> Map.new()
end
end
Setup
import Utils
input = Kino.Input.textarea("Input:")
text = Kino.Input.read(input)
data = split_all_lines(text, [" -> ", ","]) |> to_numbers()
map =
for path <- data do
for [[x1, y1], [x2, y2]] <- Enum.chunk_every(Enum.chunk_every(path, 2), 2, 1, :discard) do
cond do
x1 == x2 -> for y <- y1..y2, do: {x1, y}
y1 == y2 -> for x <- x1..x2, do: {x, y1}
end
end
end
|> List.flatten()
|> Enum.group_by(&elem(&1, 0), &elem(&1, 1))
|> Map.new(fn {k, v} -> {k, v |> MapSet.new() |> Enum.sort()} end)
P1
defmodule P1 do
def solve(map) do
Stream.iterate(
map,
fn map ->
case find_next(map, 500, 0) do
nil ->
nil
{x, y} ->
%{map | x => List.insert_at(map[x], Enum.find_index(map[x], &(&1 > y)), y)}
end
end
)
|> Stream.take_while(&(!is_nil(&1)))
|> Enum.count()
|> Kernel.-(1)
end
def find_next(map, x, y) do
c = first(map, x, y)
[l, r] = Enum.map([x - 1, x + 1], &first(map, &1, c))
case {l, c, r} do
{_, nil, _} -> nil
{l, c, _r} when l > c -> find_next(map, x - 1, l)
{_l, c, r} when r > c -> find_next(map, x + 1, r)
{_l, c, _r} -> {x, c - 1}
end
end
def first(_map, _x, nil) do
nil
end
def first(map, x, y) do
map |> Map.get(x, []) |> Enum.drop_while(&(&1 < y)) |> List.first()
end
end
P1.solve(map)
P2
defmodule P2 do
def solve(map) do
max = map |> Map.values() |> List.flatten() |> Enum.max()
Stream.iterate(
map,
fn map ->
case find_next(map, 500, 0) do
{500, 0} ->
nil
{x, nil} ->
Map.update(map, x, [max + 1], &(&1 ++ [max + 1]))
{x, y} ->
%{map | x => List.insert_at(map[x], Enum.find_index(map[x], &(&1 > y)), y)}
end
end
)
|> Stream.take_while(&(!is_nil(&1)))
|> Enum.count()
end
def find_next(map, x, y) do
c = first(map, x, y)
[l, r] = Enum.map([x - 1, x + 1], &first(map, &1, c))
case {l, c, r} do
{_, nil, _} -> {x, nil}
{l, c, _r} when l > c -> find_next(map, x - 1, l)
{_l, c, r} when r > c -> find_next(map, x + 1, r)
{_l, c, _r} -> {x, c - 1}
end
end
def first(_map, _x, nil) do
nil
end
def first(map, x, y) do
map |> Map.get(x, []) |> Enum.drop_while(&(&1 < y)) |> List.first()
end
end
P2.solve(map)