Advent of Code 2024, Day 13

Day 13 brings us LaTeX, high school math, coordinate systems, graphing, vectors, equation sets, and variable types for memory layout.

Cute pink and baby blue claw machines in an empty arcade filled with plushies.
Photo by Sergio Guardiola Herrador on Unsplash

Part of the AoC 2024 series

Claw machines and lots of irrelevant details for part 1 make this somewhat of a cute problem that takes me back to my high school math classes.

adventofcode2024/day13 at main Ā· javorszky/adventofcode2024
Contribute to javorszky/adventofcode2024 development by creating an account on GitHub.

Spoilers

Part 1

So much unnecessary information! So each button will move a certain distance in the X and Y coordinates. That makes them vectors. Because they move the set distance per push of a button, you can only have integer (whole number) number of vectors each.

I am starting to notice a bunch of patterns that Iā€™m falling into. For example previously I would parse the input in a setup code, and only once I have pristine and correctly parsed values do I instantiate structs.

I have now since figured out that the .try_from() -> Option<t> associated functions encapsulate that parsing and maybe creating a thing out of whatever I pass in.

So instead of breaking up the following input into individual lines, stripping the front, splitting the numbers by the , , stripping the X=, X+, Y=, and Y+ prefixes and then parsing the numbersā€“asā€“strings into i32, I just chuck the entire block at the Vector::try_from(input &str) -> Result<Vector, Error> associated function. It makes code so much cleaner and actually moves work to places where they make more sense.

I also learned what the difference between an associated function and a method is.

An associated function is in an impl block for a struct, but it does not take &self as an argument, which means I can call Vector::try_from, but I canā€™t call Vector::new(0, 1).try_from(). A method takes the &self as argument, which means I canā€™t call Vector::least_tokens(), because that needs an already instantiated object (wrong word, but itā€™ll do) of Vector type.

Back to the coding bit. Itā€™s essentially just math. Iā€™m going to use the first example to illustrate what I do:

Button A: X+94, Y+34
Button B: X+22, Y+67
Prize: X=8400, Y=5400

The two vectors are \( \vec{(94, 34)} \) and \( \vec{(22, 67)} \). We need some combination of these to get to point \( (8400, 5400) \). The following two equations need to hold true at the same time:

\[ \begin{align*} \begin{cases} A * 94 + B * 22 & = 8400 \\ A * 34 + B * 67 & = 5400 \end{cases} \end{align*} \]

Letā€™s subtract the top one from the bottom one after normalizing them around \( A \):

\[ \begin{align*} \begin{cases} 34 * ( A * 94 + B * 22 )&= 34 * 8400\\ 94 * ( A * 34 + B * 67 )&= 94 * 5400 \end{cases} \\ \begin{cases} A * 3196 + B * 748 &= 285600 \\ A * 3196 + B * 6298 &= 507600 \end{cases}\\ A * 3196 + B * 6298 - A * 3196 - B * 748 &= 507600 - 285600 \\ A * (3196 - 3196) + B * (6298 - 748) &= 222000 \\ A * 0 + B * 5550 &= 222000 \\ B * 5550 &= 222000 \\ B &= \frac {222000} {5550} \\ B &= 40 \end{align*} \]

Now that we have \( B \), we can plug that back into one of the starter equations:

\[ \begin{align*} A * 94 + \bold{B} * 22 & = 8400 \\ A * 94 + \bold{40} * 22 & = 8400 \\ A * 94 + 880 & = 8400 \\ A * 94 &= 8400 - 880 \\ A * 94 &= 7520 \\ A &= \frac {7520} {94} \\ A &= 80 \end{align*} \]

And voilĆ”, we have our \( A \) and \( B \) values. We can price it up, and get the correct score for that machine. All I had to do is take the input, run it through my code, and now I have my solution of 40069. Nice!

Part 2

Whoops, the coordinates of the target are so much farther away. However because I already used math and algorithms to solve part 1, part 2 was a relatively minor adjustment where I had to change the visibility of the Coordinate and ClawMachine structs to be pub(crate), some of their associated functions, and create two new versions of try_from for both of them.

On the Coordinate itā€™s mostly the same, but adds the new extra number to the parsed numbers, as the input doesnā€™t change, and in ClawMachine it calls the new try_from for the Coordinate.

The other issue was that \( 10,000,000,000,000 \) did not fit into i32 neatly, and using u64 resulted in a negative number at some point, which is kind of a big noā€“no for an unsigned number, so turned everything into i64 and f64. Thankfully both of those could take all values without further changes.

Got my two ā­ļøā­ļøs, onwards to day 14!

Spoiler photo by Dheeraj M on Unsplash