Advent of Code 2024/6
Mix.install([
{:kino, "~> 0.14.2"},
{:kino_vega_lite, "~> 0.1.9"}
])
Section
input = Kino.Input.textarea("input")
defmodule AoC2024_6 do
alias VegaLite, as: Vl
def parse(input) do
Kino.Input.read(input)
|> String.split("\n\n", trim: true)
|> Enum.map(fn part -> String.split(part, "\n", trim: true) |> Enum.map(fn row -> String.split(row, "", trim: true) end) end)
|> Enum.at(0)
end
def get(matrix, row, col) do
matrix
|> Enum.at(row, [])
|> Enum.at(col)
end
def set(matrix, row, col, value) do
List.update_at(matrix, row, fn r ->
List.update_at(r, col, fn _ -> value end)
end)
end
def start(matrix) do
row_map = Enum.map(matrix, fn row -> Enum.find_index(row, fn el -> el == "^" end) end)
row = Enum.find_index(row_map, fn e -> e != nil end)
{ row, Enum.find(row_map, fn e -> e != nil end), :up }
end
def new_coord({row, col, :up}), do: {row - 1, col}
def new_coord({row, col, :down}), do: {row + 1, col}
def new_coord({row, col, :left}), do: {row, col - 1}
def new_coord({row, col, :right}), do: {row, col + 1}
def new_direction(:up), do: :right
def new_direction(:right), do: :down
def new_direction(:down), do: :left
def new_direction(:left), do: :up
def contains_consecutive?(enum, value1, value2) do
enum
|> Enum.chunk_every(2, 1, :discard)
|> Enum.any?(fn [a, b] -> a == value1 and b == value2 end)
end
def step(matrix, {row, col, direction}, history \\ []) do
{new_row, new_col} = new_coord({row, col, direction})
cond do
(new_row < 0 || new_row == length(matrix) || new_col < 0 || new_col == length(Enum.at(matrix, 0))) == true ->
history
get(matrix, new_row, new_col) != "." && get(matrix, new_row, new_col) != "^" ->
step(matrix, {row, col, new_direction(direction)}, history)
contains_consecutive?(history, {row, col, direction}, {new_row, new_col, direction}) ->
nil
true ->
step(matrix, {new_row, new_col, direction}, history ++ [{new_row, new_col, direction}])
end
end
def draw(history, matrix) do
Enum.reduce(history, matrix, fn {row, col}, acc -> set(acc, row, col, "X") end)
end
def part_1(input) do
matrix = input
|> parse
start = start(matrix)
history = step(matrix, start, [start])
Vl.new()
|> Vl.data_from_values(Enum.map(history, fn {y, x, _} -> %{x: x, y: y} end))
|> Vl.mark(:point)
|> Vl.encode_field(:x, "x")
|> Vl.encode_field(:y, "y")
|> Kino.VegaLite.new()
|> Kino.render()
Enum.count(Enum.uniq(history |> Enum.map(fn {x, y, _} -> {x, y} end)))
end
def part_2(input) do
matrix = input
|> parse
start = start(matrix)
history = step(matrix, start, [start])
points = Enum.filter(
Enum.chunk_every(history, 2, 1, :discard),
fn [{first_row, first_col, direction}, {second_row, second_col, second_direction}] ->
(direction == second_direction) &&
(step(set(matrix, second_row, second_col, "X"), {first_row, first_col, direction}) == nil)
end
)
|> Enum.map(fn [_, second] -> second end)
|> Enum.uniq
Vl.new()
|> Vl.layers([
Vl.new()
|> Vl.data_from_values(Enum.map(history, fn {y, x, _} -> %{x: x, y: y} end))
|> Vl.mark(:point)
|> Vl.encode_field(:x, "x")
|> Vl.encode_field(:y, "y"),
Vl.new()
|> Vl.data_from_values(Enum.map(points, fn {y, x, _} -> %{x: x, y: y} end))
|> Vl.mark(:tick)
|> Vl.encode_field(:x, "x")
|> Vl.encode_field(:y, "y")
])
|> Kino.VegaLite.new()
|> Kino.render()
Enum.count(points)
end
end
AoC2024_6.part_1(input)
AoC2024_6.part_2(input)