# Advent of Code 2023 - Day 14

Hello, coding maestros! Ukrainian Erlanger at your service , and it’s a thrilling Thursday. Today marks Day 14 of our exhilarating journey through Advent of Code, presenting a fresh puzzle for you to conquer. Dive into the challenge, flex those coding muscles, and, of course, share your ingenious solutions in the comments below. Let’s keep the coding camaraderie strong and turn this Thursday into a day of triumph! Happy coding, everyone!

2 Likes

I may have made that more complicated than it needed to be with the way I represented the grid. It was kind of slick for part 1 though.

2 Likes

For part 2 found cycle manually first, then implemented search for it

``````defmodule Aoc.Fourteen do
def parse(string) do
string
|> String.split("\n", trim: true)
|> Enum.map(fn line ->
line
|> String.graphemes()
end)
end

for {line, y} <- Enum.with_index(data, 1), {i, x} <- Enum.with_index(line, 1), into: %{} do
{{y, x}, i}
end
end

def part_one(input) do
input
|> tilt()
end

def part_two(input) do
input
|> cycle(1_000_000_000)
end

defp tilt(map) do
{y_max, x_max} = maxs = map |> Map.keys() |> Enum.max()

for y <- 1..y_max, x <- 1..x_max do
{y, x}
end
|> Enum.reduce(filter_map(map), &tilt(&1, maxs, :north, &2))
|> north_weight(y_max)
end

defp filter_map(map) do
map |> Map.filter(&(elem(&1, 1) != "."))
end

defp tilt({y, x} = c, {y_max, x_max}, dir, acc) do
case Map.get(acc, c) do
"O" ->
new_acc = Map.delete(acc, c)

case dir do
:north -> roll_y(y..1, x, new_acc)
:south -> roll_y(y..y_max, x, new_acc)
:west -> roll_x(x..1, y, new_acc)
:east -> roll_x(x..x_max, y, new_acc)
end

_ ->
acc
end
end

def cycle(map, n) when is_integer(n) do
maxs = map |> Map.keys() |> Enum.max()
map = map |> filter_map()

1..n
|> Enum.reduce_while(
{map, %{}, %{}},
fn x, {acc, memo_i, memo_w} ->
case memo_i[acc] do
nil ->
new_acc = cycle(acc, maxs)
new_memo_i = Map.put(memo_i, acc, x)
new_memo_w = Map.put(memo_w, x, north_weight(acc, elem(maxs, 0)))
{:cont, {new_acc, new_memo_i, new_memo_w}}

previous ->
cycle_length = x - previous
index = rem(n - x, cycle_length) + 1
{:halt, memo_w[previous + index]}
end
end
)
end

def cycle(map, {y_max, x_max} = maxs) do
[
{:north, yx(1..y_max, 1..x_max)},
{:west, xy(1..x_max, 1..y_max)},
{:south, yx(y_max..1, x_max..1)},
{:east, xy(x_max..1, y_max..1)}
]
|> Enum.reduce(map, fn {dir, coords}, acc ->
Enum.reduce(coords, acc, &tilt(&1, maxs, dir, &2))
end)
end

defp roll_y(from..to//by = range, x, acc) do
Enum.reduce_while(range, acc, fn y, acc ->
case {y, Map.get(acc, {y, x})} do
{^to, nil} ->
{:halt, Map.put(acc, {y, x}, "O")}

{_, nil} ->
{:cont, acc}

_ ->
{:halt, Map.put(acc, {y - by, x}, "O")}
end
end)
end

defp roll_x(from..to//by = range, y, acc) do
Enum.reduce_while(from..to, acc, fn x, acc ->
case {x, Map.get(acc, {y, x})} do
{^to, nil} ->
{:halt, Map.put(acc, {y, x}, "O")}

{_, nil} ->
{:cont, acc}

_ ->
{:halt, Map.put(acc, {y, x - by}, "O")}
end
end)
end

def yx(y_range, x_range) do
for y <- y_range, x <- x_range do
{y, x}
end
end

def xy(x_range, y_range) do
for x <- x_range, y <- y_range do
{y, x}
end
end

def north_weight(map, y_max) do
map
|> Enum.map(&weight(&1, y_max))
|> Enum.sum()
end

defp weight({{y, x}, v}, len) do
case v do
"O" ->
len - y + 1

_ ->
0
end
end
end