Advent of Code 2024 - Day 04
Mix.install([
{:kino_aoc, "~> 0.1.7"}
])
Introduction
2024 - Day 04
Puzzle
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2024", "4", System.fetch_env!("LB_AOC_SESSION"))
Parser
Code - Parser
defmodule Parser do
def parse(input) do
input
|> String.split("\n", trim: true)
|> Enum.with_index()
|> Enum.reduce(%{}, fn {line, y}, map ->
line
|> String.codepoints()
|> Enum.with_index()
|> Enum.reduce(map, fn {char, x}, map ->
Map.put(map, {x, y}, char)
end)
end)
end
end
Tests - Parser
ExUnit.start(autorun: false)
defmodule ParserTest do
use ExUnit.Case, async: true
import Parser
@input """
XA
MB
"""
@expected %{{0, 0} => "X", {0, 1} => "M", {1, 0} => "A", {1, 1} => "B"}
test "parse test" do
actual = parse(@input)
assert actual == @expected
end
end
ExUnit.run()
Part One
Code - Part 1
defmodule PartOne do
@dir [{1, 0}, {1, -1}, {0, -1}, {-1, -1}, {-1, 0}, {-1, 1}, {0, 1}, {1, 1}]
def solve(input) do
IO.puts("--- Part One ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
map = Parser.parse(input)
map
|> Enum.filter(fn {_, v} -> v == "X" end)
|> Enum.map(fn {{x, y}, _} -> count_xmas(map, x, y) end)
|> Enum.sum()
end
defp count_xmas(map, x, y) do
@dir
|> Enum.count(fn {vx, vy} ->
1..3
|> Enum.map(fn v -> Map.get(map, {x + vx * v, y + vy * v}, "") end)
|> Enum.join("") ==
"MAS"
end)
end
end
Tests - Part 1
ExUnit.start(autorun: false)
defmodule PartOneTest do
use ExUnit.Case, async: true
import PartOne
@input """
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
"""
@expected 18
test "part one" do
actual = run(@input)
assert actual == @expected
end
end
ExUnit.run()
Solution - Part 1
PartOne.solve(puzzle_input)
Part Two
Code - Part 2
defmodule PartTwo do
@up [{1, -1}, {-1, -1}]
@down [{-1, 1}, {1, 1}]
@left [{-1, -1}, {-1, 1}]
@right [{1, -1}, {1, 1}]
def solve(input) do
IO.puts("--- Part Two ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
map = Parser.parse(input)
map
|> Enum.filter(fn {_, v} -> v == "A" end)
|> Enum.count(fn {{x, y}, _} -> is_xmas(map, x, y) end)
end
defp is_xmas(map, x, y) do
[@up, @down, @left, @right]
|> Enum.map(fn dir ->
dir
|> Enum.map(fn {vx, vy} ->
Map.get(map, {x + vx, y + vy}, "")
end)
|> Enum.join("")
end)
|> then(fn
["SS", "MM", _, _] -> true
["MM", "SS", _, _] -> true
[_, _, "SS", "MM"] -> true
[_, _, "MM", "SS"] -> true
_ -> false
end)
end
end
Tests - Part 2
ExUnit.start(autorun: false)
defmodule PartTwoTest do
use ExUnit.Case, async: true
import PartTwo
@input """
MMMSXXMASM
MSAMXMSMSA
AMXSXMAAMM
MSAMASMSMX
XMASAMXAMM
XXAMMXXAMA
SMSMSASXSS
SAXAMASAAA
MAMMMXMMMM
MXMXAXMASX
"""
@expected 9
test "part two" do
actual = run(@input)
assert actual == @expected
end
end
ExUnit.run()
Solution - Part 2
PartTwo.solve(puzzle_input)