12
votes
Day 2: Gift Shop
Today's problem description: https://adventofcode.com/2025/day/2
Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python with any of the "short names" listed in this page of supported languages):
<details>
<summary>Part 1</summary>
```python
Your code here.
```
</details>
To be honest, I didn't really know what I was doing with this one. Still don't have a clue how to do it in a "smart" way. I just bruteforced it, and it takes a couple seconds to run, which I'm not super happy with, but I'll take the win for tonight. Maybe I'll come back to it in the morning and see if I can figure out a nicer solution. I'm doing a lot of string copying right now, which I think is the main source of overhead.
Solution (Lily)
Brute-forced it again, but learned a lot in doing so!
Had one of my first "aha!" moments in using
string_concat/3backwards to both check if one string was a prefix of another and find the suffix that matched. No idea if it's the most efficient approach, but super neat!Prolog Solution
I made a note to learn about records, as right now I'm just using lists for everything.
This is good exercise for me to brush up on python again (as unemployed cs grad). It's surprisingly neat when it comes to data transformations using list comprehesions and method chaining. The task of repeating strings lends well to regex too which I'm kinda trigger happy to use. Anyhow, this is my first time doing AoC and I'm having a good time so far, despite forgetting basic stuff that I have to ChatGPT my way through like reading a file. Combine with type declarations and pyright/basedpyright and troubleshooting is really fast.
Solution
This is only part one. I'll do the second later.
Google Sheets
edit: faster
Here are my solutions for both parts in Perl using a feature from the relatively newly released v5.42. I always find that I lean a little heavily on regular expressions for my Advent of Code solutions.
Part 1 (Perl)
Part 2 (Perl)
Slightly compacted versions...
Part 1 (Perl)
Part 2 (Perl)
I didn't post yesterday because I was annoyed at how long it took me. I was much happier today. My part 1 took very little time to write and executed pretty quickly, but part 2 took a little longer. On my machine I averaged about 1.2s. I decided to plug in some threading (not included here) to see if I could get it faster, and got it down to ~230ms. That was a cool win for today!
Part 1
Part 2
I had a very similar solution to you, also in python. Seems like the big difference is that you're iterating from smaller to larger chunk sizes, and I'm iterating from halving through to 1/n-ing. I think they're roughly equivalent in efficiency, but yours seems more intuitive. My only advantage is that it avoids the single digit issue you ran into.
I'm not super happy with this algorithm, but it seems like everyone has done pretty much the same thing, so at least I'm not alone.
Part 1
Part 2
Elixir
I did my best to optimize my part 1 solution by filtering out large chunks of the ID ranges that would not work. (hint: the ID needs to be able to split into two parts with equal numbers of digits.)
But none of that optimization really applies to part 2, so I did basically a brute-force approach for it. It took over a second to complete before I tweaked it to process all rows concurrently using
Task.async_stream/3. Running concurrently, it takes about half a second.Parsing the input
Part 1
Part 2
Benchmarks
Without concurrency:
With concurrency:
When scanning your solution, there's definitely some standard-library stuff that I hadn't stumbled upon yet, that I maybe should be using.
I was a little unhappy with the runtime, but
async_streamwas surprisingly approachable and cut it down to ~2 seconds for both tasks (which still isn't exactly fast).I reckon doing the splitting on integers instead of strings might be the next obvious thing to improve, but I can't be bothered to do more now, time to sleep.
Code
The problem today felt about as simple as yesterday, with my logic to find invalid IDs even uglier than the stuff I wrote yesterday. At least, I'm getting a hang of the syntax of Rust and didn't need to spend much time searching the documentation.
Instead of going through all the numbers in each range, I'm sure it's feasible to iterate over the first half of each number and directly check if the resulting repeating ID is inside the range. Thinking about it, I'm feeling like this could even yield a simpler logic than what I currently have.
Part 1 and 2 (Rust)
Edit: Indeed, it's possible to keep track of the part that is going to be repeated and iterate directly over it. Based on the result, it's not exactly simpler, especially since the second part still needs the
is_invalidlogic to avoid double-counting numbers in the range. At least, it's mighty fast: it's running in ~1ms including all the overhead of starting the process and parsing the input while the previous version took ~120ms.Implementation of the sum of invalid IDs over a range for both parts
Edit 2: actually, things can be pushed even further since the sum of invalid IDs (for part 1) in the range
123000..456999is the same as the sum of numbers123..456times 101, which can be computer is O(1) using the fact that the sum of numbers from 1 to n (included) isn*(n+1)/2. Part 2 becomes even more interesting since you also want to exclude all the numbers that are invalid IDs from the sum of the consecutive numbers, which can be done using a recursive call to the function that computes the sum of invalid IDs for a given range. Thus, it's not actually necessary to have anis_validfunction for part 2 either.Trying to get a little bit of Kotlin experience in. Nice little indentation pyramid of doom in part 2, not exactly proud but it works.
Part 1
Part 2
TypeScript
I posted earlier about my misunderstanding that repeated digits could be repeated more than twice. So I came up with a decent Part 1 solution that "chops" potential IDs within the range in half (as strings), repeats the chopped value, then casts back to a number to validate.
Of course (as I predicted) Part 2 did allow for repeated sequences of arbitrary length, obsoleting my first approach. I ended up doing the following:
Parts 1 and 2
Edited to add benchmarks:
Part 1 completes in 1ms on my machine; Part 2 completes in 19ms.
More Python generator madness on Day 02! Super interesting to see how everyone approached the "has repetition" check. For my part, I used an
all()substring comparison approach forany()valid substring length.Part 1
Part 2
Part two really was painful for some reason. I couldn't wrap my mind around how to find all the duplicates when it could just be two, or three or more. So this is what I ended up with. I feel like I was going down a brute force path there for a bit before coming to this solution.
Rust - Parts 1 and 2
For now I've bruteforced both parts of my Python solution. When I'm more awake maybe I'll revisit it and try to be more clever. But simply using PyPy instead of CPython cuts my part 2 runtime from 20s to 1.5s. Good enough for now.
Part 1
Part 2
I am pretty happy with my solution on this one. Early breaking when I see a failure gets this down to ~300ms on my laptop. I am absolutely sure the same algorithm could be done in a better way if I knew Rust better.
I only have the Part 2 solution because I changed the function call to do the whole thing, not thinking about holding both parts.
Part 2
Rust
Part 1 - This was a weird puzzle for me: I don't do a lot of generating strings that match a pattern—usually it's the opposite, where I check if a string matches a pattern. For performance, generating is definitely the way to go here vs brute-force scanning all the ranges for invalid IDs. After a lot of fiddling I was able to get an invalid ID generator working, and it's pretty fast!
Part 2 - I took one look at part 2 and decided I didn't want to re-do all that fiddling to make my solution more generic. 😭 This is just naive brute-forcing and it's many orders of magnitude slower than it ought to be.
I haven’t started writing code yet, just looking at the puzzle for now. Something’s bugging me…
Can someone help me understand why
111and999are apparently NOT invalid? I thought I understood the rule but those two are throwing me off.Because in those numbers, the repeated digits are repeated more than twice
D’oh! Thanks!
That should actually make the logic a good deal simpler (at least until the Part 2 complication comes along and undoes everything…)!
You're welcome! good luck and have fun