Command Palette

Search for a command to run...

Advent of Code Day 1

Timeline

Built With

TypeScript/Rust

Introduction

Here is the question for Day 1 from Advent of Code(opens in a new tab).

My first thought was that I would probably need to use an array and the modulus operator. The question mentions that the dial only moves between 0 and 99—this kind of wrapping behavior is usually achieved through modulus arithmetic.

All AoC challenges need to read inputs, so I created an index.ts. It reads input.txt and parses it into an array so I can manipulate the data. I also structured my project by using index.ts as an entry file to test different code.

JavaScript

Part One

At first, I thought about creating two arrays: one for the instructions ("R5,L20,...") and another for the constant dial values from 1 to 99. I planned to manipulate the dial array based on the instructions. But I quickly scrapped this idea when I realized I only needed a single number variable to represent the dial position, which always starts at 50.

I started by looping through the instruction array, using string slicing to separate the direction from the value.

A quick test made me realize that while adding positive values with the modulus operator is simple, negative values are a pain. In JavaScript, the % operator is actually a remainder operator, not a true mathematical modulus. Unlike the conventional modulus which only returns positive values, JavaScript's operator can return negative values (e.g., -5 % 100 is -5, not 95).

To handle the "L" (Left) direction, I had to add an extra operation to force it back to positive:

To get the answer (how many times the dial points at zero), I just used a counter to check the value of the dial at the end of every loop.

Part Two

Part Two wants us to count the number of times the dial passed through the number 0 as well. Since we need the total of "pointing at 0" AND "passing through 0," I figured I didn't need to change the main structure. I simply needed a function to track the pass-bys.

My first thought on what counts as a "pass-by": a full rotation. For every 100 steps, it definitely passes 0.

At the end of the outer loop, I calculate the total count and reset the per-rotation counter.

I ran the program using the sample input, but I got the wrong answer. Apparently, there are many edge cases to consider.

The first issue was a false positive. In one iteration, the dial started at 0 and rotated left by 5 to position 95. This fulfilled the loop condition and increased the passByPerRotation counter incorrectly. To resolve this, I added a check: only if the dial is not starting at 0 should it count the pass-by.

But this new condition broke another test case: when the dial starts at 0 and rotates left 500 steps. This should increase the counter by 5. I had to patch that up too:

At this point, this messy code passed most test cases but still gave the wrong result on the actual input.txt.

Brute Force

One way to figure out how far off I was: create a brute-force solution that is guaranteed to be correct. You can write this yourself or ask an LLM. It simply loops through and counts every single "click" of the dial.

Comparing the results, my logic was only off by around 100. After testing different cases, I found the culprit: Start at 0, Rotate Left 150.

The problem is the loop logic. It runs the first iteration (correct), increases j to -50, and runs again because -50 is still less than 0. Mathematically correct for the loop condition, but wrong for the problem.

A simple visualization: Dial starts at 0, rotates Left 150 steps. It passes 0 once (at the 100th step) and stops at 50. My code was counting it twice.

I'm sure I could fix this with Math.abs, but the code was messy enough. I decided to start over and come up with a cleaner solution.

Initially, I did the first four days in JavaScript. But when trying to port them to TypeScript, I realized how much better TypeScript is—it forces me to write safer code. So, I decided to switch to TypeScript entirely.

TypeScript

Final Solution

First, I created a proper Euclidean modulo function to return non-negative results, saving me from "if/else" hell.

Second, calculating the pass-bys. I realized that technically, I don't care about the final position for the count. Modulus is useless here because R500 mod 100 returns 0, but that ignores the 5 rotations. I needed division: 500 / 100 = 5 rotations.

For the remaining steps (e.g., R523), I use the remainder operator and check if the final position crosses the threshold.

I used Math.floor to avoid floating-point numbers and added extra conditions for left rotations to handle edge cases:

  1. Start 20, Left 50: 20 - 50 (-30) <= 0 -> Count (passed 0).

  2. Start 20, Left 20: 20 - 20 (0) <= 0 -> Count (stopped at 0).

  3. Start 0, Left 5: 0 - 5 (-5) <= 0 -> No Count (started at 0).

The full solution is:

Refactored Version

Here is a refactored version I built with Gemini. It simplifies the logic by using a continuous coordinate system to calculate the absolute difference. It took some time to develop the intuition for this, but it's much cleaner.

Rust

Part One

Since I solved this in JS first, my main Rust function uses that same logic. However, I also wrote a solve() function which is refactored. It converts the input into a signed move_amount (positive for Right, negative for Left).

Crucially, Rust has a built-in Euclidean remainder function: rem_euclid. This is basically the custom mod function I wrote in JS, but native. It makes the code very concise.

Part Two

To solve Part Two, I ported the pass-by function. It uses the same logic as the TypeScript version, just with Rust syntax.