Advent of Code 2024 - Day 13
Mix.install([
:kino_aoc
])
Introduction
2024 - Day 13
Puzzle
{:ok, puzzle_input} =
KinoAOC.download_puzzle("2024", "13", System.fetch_env!("LB_AOC_SESSION"))
Parser
Code - Parser
defmodule Parser do
@reg ~r/\d+/
def parse(input) do
input
|> String.split("\n", trim: true)
|> Enum.chunk_every(3, 3, :discard)
|> Enum.map(fn [button_a, button_b, prize] ->
[[x_a], [y_a]] = Regex.scan(@reg, button_a)
[[x_b], [y_b]] = Regex.scan(@reg, button_b)
[[x_p], [y_p]] = Regex.scan(@reg, prize)
[x_a, x_b, x_p, y_a, y_b, y_p]
|> Enum.map(&String.to_integer/1)
end)
end
end
Tests - Parser
ExUnit.start(autorun: false)
defmodule ParserTest do
use ExUnit.Case, async: true
import Parser
@input """
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
"""
@expected [[94, 22, 8400, 34, 67, 5400], [26, 67, 12748, 66, 21, 12176]]
test "parse test" do
actual = parse(@input)
assert actual == @expected
end
end
ExUnit.run()
Shared
defmodule Shared do
def solve_equation([a1, b1, c1, a2, b2, c2]) do
d = a1 * b2 - a2 * b1
if d == 0 do
nil
else
dx = c1 * b2 - c2 * b1
dy = a1 * c2 - a2 * c1
rem_x = rem(dx, d)
rem_y = rem(dy, d)
x = div(dx, d)
y = div(dy, d)
if x < 0 or y < 0 or rem_x != 0 or rem_y != 0 do
nil
else
{x, y}
end
end
end
end
Part One
Code - Part 1
defmodule PartOne do
def solve(input) do
IO.puts("--- Part One ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
input
|> Parser.parse()
|> Enum.map(fn params ->
case Shared.solve_equation(params) do
nil -> 0
{a, b} -> a * 3 + b
end
end)
|> Enum.sum()
end
end
Tests - Part 1
ExUnit.start(autorun: false)
defmodule PartOneTest do
use ExUnit.Case, async: true
import PartOne
@input """
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279
"""
@expected 480
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
def solve(input) do
IO.puts("--- Part Two ---")
IO.puts("Result: #{run(input)}")
end
def run(input) do
input
|> Parser.parse()
|> Enum.map(fn [x_a, x_b, x_p, y_a, y_b, y_p] ->
x_p = 10_000_000_000_000 + x_p
y_p = 10_000_000_000_000 + y_p
case Shared.solve_equation([x_a, x_b, x_p, y_a, y_b, y_p]) do
nil -> 0
{a, b} -> a * 3 + b
end
end)
|> Enum.sum()
end
end
Tests - Part 2
ExUnit.start(autorun: false)
defmodule PartTwoTest do
use ExUnit.Case, async: true
import PartTwo
@input """
Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400
Button A: X+26, Y+66
Button B: X+67, Y+21
Prize: X=12748, Y=12176
Button A: X+17, Y+86
Button B: X+84, Y+37
Prize: X=7870, Y=6450
Button A: X+69, Y+23
Button B: X+27, Y+71
Prize: X=18641, Y=10279
"""
@expected 875_318_608_908
test "part two" do
actual = run(@input)
assert actual == @expected
end
end
ExUnit.run()
Solution - Part 2
PartTwo.solve(puzzle_input)