👪 - 2025 DAY 7 SOLUTIONS - 👪
Day 7: Laboratories
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
QIR (Quantum Intermediate Representation)
Nah, just kidding - I used Rust. The only tricky part seemed to be finding time on a Sunday to get it coded!
Part 1 I swept down with a bool array for beams and part 2 I swept up with a score array and summed when it split (joined).
Rust
Dynamic programming? Nah, just keep track of the number of overlapping beams and part 2 becomes no different to part 1.
View on github
Ahh I knew there would be some simple combined representation like this but couldn't be bothered. Nice!
Uiua
A bit late getting to this, but happy with this solution, despite it being nothing more than just building/summing all paths. As usual, you can drop your own file onto that solution.
Man, your solution is so much cleaner than the hell I was trying (and failing) to coerce into doing what I wanted. Curse my inability to think bottom-up and array-oriented at the same time!
When I compare it to some other Uiua solutions on the Discord I feel like I'm still just banging rocks together.
Haskell
That was a fun little problem.
And here's a super-simple version, because why not.
Python
Not too difficult, especially since there are no edge cases (particles leaving the grid, adjacent splitters).
My initial solution mutated the input for the simulation which I cleaned up after by creating an array that would record the number of particle paths at every column location + some other optimizations. I chose to implement both parts in the same function because they share the majority of the logic.
::: spoiler click to view code
:::
Uiua
Heavily
copiedahem, inspired by @[email protected]'s solution :)Nim
Another simple one.
Part 1: count each time a beam crosses a splitter.
Part 2: keep count of how many particles are in each column in all universes
(e.g. with a simple 1d array), then sum.
Runtime:
116 μs95 µs86 µs::: spoiler old version
:::
Update: found even smaller and faster version that only needs a single array.
Update #2: small optimization
Full solution at Codeberg: solution.nim
Ah, it took me looking at your updated Codeberg version to understand this - you looked at part two in the opposite way than I did but it comes out the same in the end (and yours is much more efficient).
Kotlin
Tried recursive for part1, didn't work. LUCKILY for once I was smart and committed anyway, as it was the right solution for part2! I was losing my mind for a bit though as I had originally written my method with Integers...
::: spoiler Solution
:::
full code on Codeberg
I didn't do recursion on part 1, so my part 1 and 2 were fairly different.
(Browser-based) Javascript
I got lazy for part 1 and stuffed the beam head's coordinates into a string instead of reusing my implementation that puts them into an int, gambling on part 1 not needing that extra bit of speed.
I did a similar mistake for part 2 as I did on day 6; I wrote a(n overkill) solution for keeping track of the entire path each beam/split follows - which involved cloning each array-storing-a-beam's-path at each split. Whereas on day 6 I got an "out of memory" error, this time I locked up my computer 💪 🔥 (Control+C doesn't exist in the browser console, and Control+W to close the tab wasn't getting through). Was very disappointed to realize we only need to keep track of the number of beams present at each position/index/abscissa without caring about which path they took to reach said index.
::: spoiler Code
:::
Surprised you didn't trigger the OOM killer. Windows?
Naw, linux. I had a Youtube video running in the background that might have made the problem worse. I could switch desktops and move the mouse (albeit with a lot of lag and jitter) but not close the tab or even hard-close the browser.
Running the same code on my macbook triggered the "a script on this page is slowing down firefox, do you want to wait some more or pause and debug it?" prompt.
Either the OOM killer is not properly set up on my desktop, or it was actually a hot CPU loop that caused the lock-up and not memory pressure. Or some sort of hybrid cpu+mem problem? The code was cloning ever-longer arrays in a for-loop, and from my understanding the event loop for a browser page/tab is not only single-threaded but contains both UI events and script execution (unless you explicitly/specifically shell out to a web worker or the like).
Possibly using up swap space first?
This might be the actual culprit; I don't think I have any swap space allocated (
htopalways displays0/0for it). I have 48 gigs of RAM, and the "out of memory" error I got on a previous day's problem didn't provoke a single slowdown, freeze, or program crash...Go
Oh my scheduling God! Part 1 was easy. Then for part 2 I started tracing each particle one by one using goroutines, but spawning billions of goroutines seemed to make my poor laptop sad. So I implemented a whole thread pool with process management and stuff, but nothing worked. So at the end I started doing the unthinkable: using my brain.
It seems we can just reuse the same idea as part 1 one but with a clever counting scheme. Thing works and is as fast as part 1. I'm both happy and deeply sad not to have been able to leverage Go's supposed killer features - which are actually rarely useful when programming things other than servers tbh.
Here we goooooo:
::: spoiler day07.go
:::
~32B coroutines does sound like it might make any CPU a bit sad :D
Yeah, the process was terminated by Linux in about 2-3 mins x)
C
Accidentally solved part 2 first but had the foresight to save the code. Otherwise my solution looks similar to what other people are doing, just with more code 😅
::: spoiler Code
:::
Haskell
Rust
Part 1 was quite straightforward: just keep track of a frontier of every beam, and keep following it, adding splitter locations to a set. Easy.
I had to give some thought to Part 2 and ended up doing dynamic programming: for every position, keep track of how many timelines go through it, solving recursively where necesary.
I went for recursion (and a simple graph node class) but my favourite solution is transfer matrices one that I have seen some people do
Go
Now I'm behind by 1 day, will try to catch up.
For part 2 I spent a good while thinking about it, then when I convinced myself my plan could work, struggled a bit with the implementation. But it worked in the end. Basically grid[i][j] is how many different ways you can reach a cell. Start at 1 on the S cell, then propagate the values down and keep adding up the nums when you reach cells through different paths. The answer is the sum of the nums in the last row.
::: spoiler spoiler
:::
Rust
Not too difficult, but my pt1 approach didnt work for pt2. Small amount of tweaking and it was back in action. This should be a really good one for visualisations, so I am definitely going to try do something. Maybe colorise the nodes based on how many paths go through it? Definitely an animation of the trickle down. Looking forward to seeing everyone elses visualisations as well!
::: spoiler spoiler
:::
Uiua
Part 1 was fun, though I had quite some frustration with getting the loop work the way I wanted it to.
For part 2 I didn't actually need to modify that much, just pass another function to deal with the list of tachyons: part 1, remove duplicates, part 2, don't remove and count at the end. Easy. Or so I thought.
I realized something was wrong when it just wouldn't stop running and the fan got louder.
Today I started over from scratch and decided to solve both parts at once since I basically got the counts for each row anyways, so I just had to change the way the splitting was handled.
There was one occasion where I got an error that the resulting array would be too big (~4GB) but at least I got a warning instead of seeing RAM usage spike suddenly :P
Run with example input
:::spoiler Code
:::
And for completeness sake: :::spoiler Previous attempt Edit: Tried to scrape the old code together but seems like it doesn't actually work like this. Probably just some small thing I missed but I don't have the mental energy to take a look at this mess again :P
:::
Kotlin
Part 1 is easily solved by simulating the beams and modifying the grid in-place.
This does not work for part 2, however. Here I opted for a BFS algorithm, moving down row-by-row.
Similar to other solutions, I store the amount of incoming paths to a splitter, so that I can reference it later. Practically, the two beams from each splitter are representatives of all incoming beams. This reduces the search complexity by a lot!
The final count of timelines is the sum of the array that collects the incoming beam counts for each bottom-row of the diagram.
Code on GitHub ::: spoiler Code
:::