12
votes
Day 9: Smoke Basin
Today's problem description: https://adventofcode.com/2021/day/9
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>
I hope it didn't cause you too much frustration and this doesn't seem mean spirited, but that bug in particular is really, really funny.
Python golf, day 9. Tips/improvements always welcome.
Part 1
Recursion came in handy for part 2.
Part 2
Edit: Today is day 9, not day 8 lol. I am tired.
Day 09 (Rust)
Looks like Eric is giving us a simple one to relax after yesterday's tough nut.
That said, I'm sure there is some advanced trickery that makes it possible to speed things up, but today I solved everything in the most-obvious way possible.
Imports & Parsing
Helper functions
Solutions
Hey it's the first map puzzle!
Since I'm using Ruby again, I went ahead and pulled in a utility class for working with two-dimensional grids that I made for previous years.
The first part was pretty straight forward, but I lost a bit of time in the second part just being a little rusty on exactly how I needed the algorithm to behave.
Part 1 Ruby
Part 2 Ruby
I ran into a minor hitch in that I initially wanted to use an actual
Set
object for the visited/open sets, but the default implementation in the stdlib doesn't really support easily removing an arbitrary value the way I can withArray#shift
/Array#pop
(since a set is unordered by definition, and shift/pop rely on the collection having an order). Arrays work fine, but I have to go a bit out of the way to make sure I don't end up with duplicate entries.With problems like these I swear that my biggest problem is always keeping X and Y straight.
Day 9 - Part A - Python
Part A wasn't too bad, just built a grid with defaultdicts and searched it to find the low points. Mostly just keeping track of the boundaries and what-not. At some point I managed to swap my x and y axes, but I just ran with it.
Interestingly, using the default dict grid instead of my normal stacked lists meant that going out of bounds would automatically generate a new entry into the dictionary and then crash because the dictionary size changed mid-loop, instead of just crashing due to an array out of bounds exception
Day 9 - Part B - Python
This was a bit more difficult. I used the low points I generated in part A to build a list of starting points. Then I recursively searched outward from those points, adding any nearby locations that weren't already part of the basin. When there was nothing left to find I just took the size of the basin and shoved it into a list.
Tips
Make sure to try to keep X and Y straight. It's alright if you flip them from what the input is, but you need to be consistent about how YOU use them
There are only 4 directions neighboring each point, but sometimes those checks will send you out of bounds of your grid. Think about how to stop your program from crashing or giving strange results when that happens
If you're not careful about how you search for the basins, you might end up in an infinite loop of checking for locations that are already stored in your basin
Nothing fancy here.
Part 1
Split the input with
then pop this in and drag it around for the 100x100
I hate formulas like this
I shit you not. I looked at part two and figured it was impossible with a spreadsheet. I figured I'd just eyeball it --- so I styled the 9s as black and looked for what I thought were the largest areas. I selected a bunch of cells, documented the count... after five of these I was over it. I took the top three, multiplied, and bam -- topaz liked it. Not as fulfilling as sorting out yesterday's, but I'll take it.
JavaScript
Repo Link
Part 1
This was pretty straightforward. The only trick I used was to pad the heatmap to avoid bounds checking.
Part 2
For this part, once you have the low points, all you need to do is perform a walk or traversal from each low point until you run out of locations (ie. no more
9
s). For this, I used a traditional iterative frontier based approach.In terms of JavaScript there were still a few gotchas that I am still learning. For instance, adding an Array to a Set works, but it compares the reference/pointer instead of the contents, so you can never find the Array again. To work around this, I gave each location or node an integer identifier based on its row and column.
Classic flood fill.
POSIX AWK doesn't have
asort()
so getting the highest 3 numbers was jankier than it had any right to be.Part 1
Part 2
Rust
Rust
Point2D
I didn't even bother posting my code for yesterday ( I didn't finish part 2 until this morning) since my code was so gross, but I enjoyed today's problems!
Rust (both parts)
I probably could've used structs but I think my code is pretty readable and reasonably concise as it is :)
Data Structure
Today is the 2nd day I've had prior engagements which mean I was very late starting. I'm never going to compete for the top 100 but I'm not going to do well on any of the private leaderboards I joined now. Oh well.
I converting every digit of my input into a number and made a hash table to map point -> height. It's actually not a graph.
Part 1
The strategy is to find points lower than all their neighbours. Don't be stupid like me and think there must be 4 regions in your input because the example had 4.
I didn't bother with bounds checking generating neighbours because height is 9 or less, so I used a default height of 10 for missing neighbours.
Part 2
I generated a list of all basin sizes starting from each of the lowest points. Basin size is the number of visited points using a basic DFS. I ignored any points over 8 (which excludes the default of 10 for missing points again.)
Python
What problem I had.
First. From yesterday second half-day to today firth half-day I was without enthernet because one clever man somewhere cut enthernet wire.
Second. I somewhy thought that side cell must +1, not just bigger. I found out it only after vizualization which is included here.
Well, I think I know a way I can solve part 2, and I am going to have to resist looking up anything about flood filling now that I've scrolled through here because I'd like to do it the hard way first. I did part 1 very naively, which worked okay, but I am going to have to come back to part 2 tomorrow evening and reply with it then. Very interested to see what the actually good solutions are when I can let myself read them!
Part 1
Okay I did it! I realized throughout that it could be better done and so could all the code I did for the first part but I did it!
Part 2 diff
This problem, in my opinion, has a simplistic elegance to it, in that it can be done in many different ways. Recursion, using a fill algorithm, or even just multi-pass scanning. All just a matter of optimization, making this problem accessible to all skill levels of programmer.
Mine was probably not the best solution, but I had a pretty proud moment when I realized that this type of solution would have been pretty much completely out-of-reach for me two years ago.
Part 1
Part 2
Elixir
I'm using a project that I started last year, so I took advantage of a
CharGrid
module that I previously defined to parse and work with a grid of characters. It still took a while because 1) I was too stubborn to look up and implement a flood-fill algorithm, and 2) iteratively building a collection while simultaneously changing the collection you're pulling values from does not lend itself to a FP/immutable data structures approach. I had to write two custom recursive functions to accomplish this.The first time I ran it, partial basins were getting merged together when they shouldn't, and it took me a while to realize I was doing a set union where I should have been doing an intersection. It solved the puzzle successfully after that.
Part 2 runs in about 1.3s. I wonder how much faster it would be if I'd used a real flood-fill algorithm...
Both parts
CharGrid
moduleThis is me LARPing as a core library contributor 🤓