Day 4
Mix.install([
{:kino, "~> 0.14.2"}
])
Part 1
https://adventofcode.com/2024/day/4
input = Kino.FS.file_path("day4_input.txt")
|> File.read!()
|> String.trim()
|> String.split("\n")
|> Enum.map(fn line ->
String.codepoints(line) # Split characters
end)
defmodule Searcher do
def next_pos({row, col}, direction) do
{row, col} = case direction do
:r -> {row, col + 1}
:l -> {row, col - 1}
:d -> {row + 1, col}
:dr -> {row + 1, col + 1}
:dl -> {row + 1, col - 1}
:u -> {row - 1, col}
:ur -> {row - 1, col + 1}
:ul -> {row - 1, col - 1}
end
if row < 0 or col < 0, do: raise("Illegal move.")
{row, col}
end
def is_word_at?([letter], array, pos, _), do: letter == value_at(pos, array)
def is_word_at?([letter | rest], array, pos, direction) do
try do
^letter = value_at(pos, array)
new_pos = next_pos(pos, direction)
is_word_at?(rest, array, new_pos, direction)
rescue
_ -> false
end
end
def value_at({row, col}, array) do
Enum.at(array, row) |> Enum.at(col)
end
def get_all_positions(letter, array = [a_row | _]) do
Enum.map(0..length(array) - 1, fn row ->
Enum.map(0..length(a_row) - 1, fn col ->
if letter == value_at({row, col}, array), do: {row, col}
end)
end)
|> List.flatten()
|> Enum.filter(& &1)
end
end
directions = [:r, :l, :d, :dr, :dl, :u, :ur, :ul]
word = ["X", "M", "A", "S"]
all_x_positions = Searcher.get_all_positions(List.first(word), input)
result = all_x_positions
|> Enum.map(fn pos ->
Enum.map(directions, fn dir ->
Searcher.is_word_at?(word, input, pos, dir)
end)
end)
|> List.flatten()
|> Enum.filter(& &1)
|> Enum.count()
Part 2
defmodule XSearcher do
import Searcher
def is_valid_x_mas?(a_pos, array) do
try do
diag1 =
[next_pos(a_pos, :ur), next_pos(a_pos, :dl)]
|> Enum.map(fn pos -> value_at(pos, array) end)
diag2 =
[next_pos(a_pos, :dr), next_pos(a_pos, :ul)]
|> Enum.map(fn pos -> value_at(pos, array) end)
"M" in diag1 and "S" in diag1 and "M" in diag2 and "S" in diag2
rescue
_ -> false
end
end
end
a_positions = Searcher.get_all_positions("A", input)
validity = Enum.map(a_positions, fn pos ->
XSearcher.is_valid_x_mas?(pos, input)
end)
result = validity |> Enum.filter(& &1) |> Enum.count()