Advent of Code 2024 - Day 04
Mix.install([
{:req, "~> 0.5"},
{:benchee, "~> 1.3"}
])
Input
opts = [headers: [{"cookie", "session=#{System.fetch_env!("LB_AOC_SESSION")}"}]]
puzzle_input = Req.get!("https://adventofcode.com/2024/day/4/input", opts).body
grid =
String.split(puzzle_input, "\n", trim: true)
|> Enum.map(fn row ->
String.codepoints(row)
|> Enum.with_index()
|> Enum.map(fn {char, index} -> {index, char} end)
|> Map.new()
end)
|> Enum.with_index()
|> Enum.map(fn {char, index} -> {index, char} end)
|> Map.new()
Grid
defmodule Grid do
defp all_dirs, do: diagonal_dirs() ++ orthogonal_dirs()
defp diagonal_dirs, do: [{-1, -1}, {1, -1}, {1, 1}, {-1, 1}]
defp orthogonal_dirs, do: [{0, -1}, {1, 0}, {0, 1}, {-1, 0}]
defp flip_dir({col, row}), do: {-col, -row}
defp move_dir({col_modifier, row_modifier} = _dir, {col, row} = _pos) do
{col + col_modifier, row + row_modifier}
end
def check(grid, pos) do
for dir <- all_dirs() do
check(grid, dir, pos)
end
|> Enum.sum()
end
def check_x(grid, pos) do
result =
for dir <- diagonal_dirs() do
check_x(grid, dir, pos)
end
|> Enum.sum()
if result > 1, do: 1, else: 0
end
def check_x(grid, dir, pos) do
start_pos = dir |> flip_dir() |> move_dir(pos)
case build_sequence(grid, 3, start_pos, dir) do
"MAS" -> 1
_invalid -> 0
end
end
def check(grid, dir, pos) do
case build_sequence(grid, 4, pos, dir) do
"XMAS" -> 1
_invalid -> 0
end
end
defp build_sequence(grid, amount, start_pos, dir) do
Enum.reduce(0..(amount-1), {"", start_pos}, fn _index, {text, pos} ->
current_text = at(grid, pos)
new_pos = move_dir(dir, pos)
{text <> current_text, new_pos}
end)
|> elem(0)
end
def at(grid, {col, row} = _pos) do
get_in(grid, [row, col]) || ""
end
end
col_size = Map.get(grid, 0) |> Map.keys() |> length()
row_size = Map.keys(grid) |> length()
col_range = 0..(col_size - 1)
row_range = 0..(row_size - 1)
Puzzle 1
puzzle_1 = fn ->
Enum.reduce(row_range, 0, fn row, row_acc ->
Enum.reduce(col_range, row_acc, fn col, col_acc ->
col_acc + Grid.check(grid, {col, row})
end)
end)
end
puzzle_1.()
Puzzle 2
puzzle_2 = fn ->
Enum.reduce(row_range, 0, fn row, row_acc ->
Enum.reduce(col_range, row_acc, fn col, col_acc ->
col_acc + Grid.check_x(grid, {col, row})
end)
end)
end
puzzle_2.()
Benchmarks
Benchee.run(%{
"puzzle_1" => fn -> puzzle_1.() end,
"puzzle_2" => fn -> puzzle_2.() end
})
Name |
ips |
average |
deviation |
median |
99th % |
puzzle_1 |
15.40 |
64.94 ms |
±3.90% |
64.37 ms |
82.61 ms |
puzzle_2 |
27.07 |
36.94 ms |
±12.27% |
36.32 ms |
73.06 ms |