π - 2025 DAY 3 SOLUTIONS - π
Day 3: Lobby
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465
Uiua
You can run this in Uiua Pad
::: spoiler Explanation You're always looking for the highest digit far enough from the end of the string that there's space to pick the remaining number of digits. So the algorithm is very straightforward: just temporarily exclude the last n-1 characters off the end of the string and look for the first instance of the largest digit in that substring, store that char and behead the string at that point. Repeat n times.
I cant understand your code, but your description of your algorithm was very clear.
Thanks, although it only really became clear after I'd written it. I am thinking about doing a more detailed explainer post based on one day's answer to help people get a feel for it.
Rust
My first version worked with numbers, but since I was still sick of yesterday's pow(10) calls, I changed it to use ascii for the second half - the nice thing is that means it can work with hex input with no modification!
Clippy was complaining about "needless_range_loops", but it's way more readable this way.
Haskell
Yay, dynamic programming!
Version 2. I realized last night that my initial approach was way more complicated than it needed to be...
Kotlin
First day this year where I am very happy with my solution. Just super simple, recursive string building.
::: spoiler Solution
:::
full code on Codeberg
Today was interesting. My first thought was that part 2 would be a lot more complex at first glance. But I realized that my solution for part 1 worked almost out of the box for part 2.
I was also pleased to see that the algorithm ran in 1ms, which was a good deal faster than just parsing the input.
Nice! I thought about using numbers but then decided that strings are a lot easier to deal with.
I was curious, so I ran yours and it is only like 3-4ms slower. I was honestly surprised it was that close.
Just goes to show that we're often wrong when estimating and the only real way to know is to benchmark.
My version used strings as well, and I thought that as I was comparing small integers either way, it made sense to stay in ASCII as the strings were already easy to index, and it meant I could skip parsing input numbers, only needing to parse output numbers so they could be summed.
I did start with numbers so I could convert it back to compare, but it's so fast (the whole thing takes 1ms - and that's reading/parsing the input twice) that it's almost a micro benchmark.
Yeah, I vaguely remember reading something about how close string splitting is to all the logarithm math in splitting numbers, and since then I've just always used strings because that's way more intuitive to me lol
C
Surprise, O(n^12) solutions don't scale! But then it was delightful when the realization hit that the solution is actually very simple to implement - just keep removing the first digit that is followed by a higher one.
Repo link
I'm still having to finish yesterday's x86-16 assembly implementation, for which I had to write some support code to deal with large numbers as strings. That will come in useful today, too!
Python
This was the easier one for me out of the first 3 days. Cleaned up my solution before posting for better readability:
Janet
Keep in mind, Codeberg might still be under a DDoS attack
Haskell
Usually, I get up for AoC, way earlier than I normally would. But today I had to get up at exactly AoC time. I ended up postponing the puzzles until now:
::: spoiler Complete, Dependency-Free and Runnable Program It reads from stdin and writes the both solutions on a separate line to stdout.
:::
c
I love the easter egg jokes you add to your code :)
Nim
Runtime: ~240 ΞΌs
Day 3 was very straightforward, although I did wrestle a bit with the indexing.
Honestly, I expected part 2 to require dynamic programming, but it turned out I only needed to tweak a few numbers in my part 1 code.
Full solution at Codeberg: solution.nim
Go
I usually write a little helper library to bootstrap the input reading process and sometimes even the downloading of the input file. So here it is:
::: spoiler utils.go
:::
And here comes the solution to day 3:
While I am quite an adaptable person and I learn to program quickly in about all the languages I've tried, I'm still at the beginning of my journey with Go. It does feel like the language is trying to resist me being clever at every corner. I understand the reasons, why not, but damn it does make the development a bit frustrating at times
(Browser-based) Javascript
For part 2, I eagerly wrote a nice, clean, generic, functional depth-first search, only to get an out of memory error π. Note the top-level code blocks: they scope the variables declared inside them, allowing me to run the whole script repeatedly in the console without getting "redeclared variable name" errors.
Python
I had a hunch that part 2 would just be part one with more places and immediately factored that code into a separate method. Overall quite happy with the compact code.
My original solution for part 1 was just removing the last digit, get the highest number, cut off everything up to and including that first number, get the highest number again.
Once I did part 2 I realized I can just throw in a loop, cut off parts of the end so there's enough numbers left for the subsequent iterations and keep the rest the same.
Now it works for any number of batteries and all you'd need to change is the number after
Total!:DOnline pad: AoC-2025-D3
You can even use your own input by uploading a file (make sure it's using LF line endings only with a trailing one at the end) and replacing the example input with this:
&rs inf &fo "input-file.txt":::spoiler Code
:::
I'm still struggling to learn Rust, and also to figure out a good solution to these problems, but here's mine anyway:
I'm really proud of my solution to part 2. When I first read it, I was freaking stumped, I had no idea how to approach it. I was wondering if I was going to wind up resorting to brute forcing it in factorial time. But then I had an idea on the way home, and I one shotted it! (Got a few tests on the example but first try on the actual input)
Part 1 for completeness:
Futhark
I am on my way to re-do all previous days in Futhark and complete the Rest of AoC, hopefully.
::: spoiler Script to Generate input for Futhark
:::
Kotlin
I'm late to the party but I hope some of you will still be inspired by my submisison. This is an iterative solution. I began with a recursive solution that worked but I noticed that it should really be rewritten in an iterative way. The solution is also pointlessly optimized, to some degree, but that's just what I like to do. π
The logic follows a simple pattern of knowing which window of the battery bank to search in. Given the amount of batteries that remain to be turned on, if you were to turn on the last battery in the window, you'd need to turn on all the remaining batteries. So the window begins at one position past the prior battery and ends at the last battery you actually can choose to turn on. Once that has been turned on, all remaining ones need to be turned on. The window can only actually shrink to at least one position.
::: spoiler Code inside
:::
Its not a race, its a journey! Keep it up!
Rust
Seeing some of the other solutions in this thread, there are definitely simpler (and probably still faster) solutions possible, but I first sorted the bank by the highest batteries (keeping the index information) and then used a recursive greedy algorithm to find the largest battery that still follows the index order.
View on github
Haskell
I think I could have avoided the minimumBy hack by doing another reverse and changing the indices.
This problem was quite straightforward; I think it probably could have been day 1. The only interesting thing here is the use of
valuesandmultiple-value-bind, which are the standard way in Common Lisp to give a function a primary return value but also one or more advisory return values. This lets you return a rich data structure for consumers who want it, but consumers who don't want it don't need to parse that entire data structure to get the primary return value.Seems i missed the faster solutions, but i did get this down to a respectable 400ms. edit: 400ms was not respectable, mykl's method took 1ms. Mine was close though, with a bit more brain and optimisation I got there.
And the bot worked all by itself!