Advent 2024 - Day 4
Mix.install([
{:kino, "~> 0.14.2"}
])
Setup
input = Kino.Input.textarea("Please paste your input file")
input = input |> Kino.Input.read()
rows = String.split(input, "\n")
row_count = length(rows)
col_count = Enum.at(rows, 0) |> String.length()
input = input
|> String.replace("\n", "")
|> String.graphemes()
|> Enum.map(&(String.to_atom(&1)))
input
Part 1
x_locations = input
|> Enum.with_index()
|> Enum.filter(fn {char, _index} -> char == :X end)
defmodule WordSearchV1 do
@next %{
:X => :M,
:M => :A,
:A => :S
}
def navigate({input, row_count, col_count} = board, {x, y}, vec, expecting) do
if (x < 0 or x > row_count) or (y < 0 or y > col_count) do
false
else
index = row_count * y + x
val = Enum.at(input, index)
if val == expecting do
if val == :S do
true
else
{x_mod, y_mod} = vec
pos = {x + x_mod, y + y_mod}
navigate(board, pos, vec, Map.fetch!(@next, expecting))
end
else
false
end
end
end
def find_xmases({_input, row_count, col_count} = board, start_index) do
i_x = rem(start_index, col_count)
i_y = div(start_index, row_count)
for x <- -1..1 do
for y <- -1..1 do
if x == 0 && y == 0 do
false
else
vec = {x, y}
pos = {i_x + x, i_y + y}
navigate(board, pos, vec, :M)
end
end
end
|> List.flatten()
|> Enum.filter(&(&1))
end
end
for {_x, x_location} <- x_locations do
WordSearchV1.find_xmases({input, row_count, col_count}, x_location)
end
|> List.flatten()
|> Enum.count()
Part 2
a_locations = input
|> Enum.with_index()
|> Enum.filter(fn {char, _index} -> char == :A end)
defmodule WordSearchV2 do
def get_value_from_pos({input, row_count, col_count}, x, y) do
if x < 0 or x >= col_count or y < 0 or y >= row_count do
nil
else
index = row_count * y + x
Enum.at(input, index)
end
end
def find_x_mases({_input, row_count, col_count} = board, center) do
c_x = rem(center, col_count)
c_y = div(center, row_count)
if c_x - 1 < 0 or c_x + 1 > col_count or c_y - 1 < 0 or c_y + 1 > row_count do
false
else
top_left = get_value_from_pos(board, c_x - 1, c_y - 1)
top_right = get_value_from_pos(board, c_x + 1, c_y - 1)
bottom_left = get_value_from_pos(board, c_x - 1, c_y + 1)
bottom_right = get_value_from_pos(board, c_x + 1, c_y + 1)
left_pair = case {top_left, bottom_right} do
{:M, :S} -> true
{:S, :M} -> true
_ -> false
end
right_pair = case {top_right, bottom_left} do
{:M, :S} -> true
{:S, :M} -> true
_ -> false
end
is_valid = left_pair and right_pair
is_valid
end
end
end
for {_x, a_location} <- a_locations do
WordSearchV2.find_x_mases({input, row_count, col_count}, a_location)
end
|> List.flatten()
|> Enum.filter(&(&1))
|> Enum.count()