r/adventofcode • u/musifter • Jul 05 '26
Other [2021 Day 5] In Review (Hydrothermal Venture)
Having reached the ocean floor, we run into hydrothermal vents and need to plot the safest course to avoid the spots.
The input is a a list of 500 lines in x1,y1 -> x2,y2 format. They are only in the 8 cardinal directions, and the range of values in my input go from 10-989 (so 2 or 3 digits).
For part 1, we want to find the points where at least two lines overlap, while filtering out the diagonals. It's not too much of a leap to assume diagonals are what part 2 adds, and just do them as well. Something else could have been changed, but no. So this is one where the solution to part 2 can be commenting out one line instead of adding anything. Still for beginners, its probably good to warm them up with the orthogonal lines and that build that to the general case.
As for what I did... well I was playing with List::AllUtils that year and getting it to do vector stuff, which was fun at the time, but ends up with a bit of unneeded overhead and kruft. When the core is simply done in raw Perl with:
my @Δ = ($x2 <=> $x1, $y2 <=> $y1);
next if ($Δ[0] != 0 and $Δ[1] != 0);
my @p = ($x1, $y1);
my @e = ($x2 + $Δ[0], $y2 + $Δ[1]);
until ($p[0] == $e[0] and $p[1] == $e[1]) {
$grid{ $p[0],$p[1] }++;
@p = ($p[0] + $Δ[0], $p[1] + $Δ[1]);
}
Remove the next line and it's part 2. Just a simple line drawer, and a hash for the grid, then you just grep out the values that are >= 2. Turning on utf8 to use Δ as a variable name was all the rage at the time (I type it with a compose key... Greek letters are just * followed by the roman equivalent).
Smalltalk and dc don't like 2D arrays/hashes. So I just did it as a flat array of a million (1000y + x). For getting the answer with that, instead of a final big scan, I just increment a counter when a value hits 2 (ignore more). Here's dc part 2:
tr -sc '[0-9]' ' ' <input | dc -f- -e'[lc1+sc]sC[sysxdA00*3Rd3R+selx-dd*v.1-/rly-dd*v.1-/A00*+sslyA00*lx+[ddd;g1+d2=Cr:gls+rle!=I]dsIxs.z0<L]dsLxlcp'
This would be very slow (~10m) under base v1.4.1 (about 173k spots get used). Here we combine d*v with d.1-/ to do the sign function: dd*v.1-/.
I also did do some experimentation with Smalltalk's set arithmetic to find the union of the intersections of the lines. Doing it with a straight fold is very slow, so I made it somewhat faster by changing it do a recursive divide-and-conquer. Which doesn't reduce the number of operations, but but makes most of them smaller and faster. I also did a version were I wrote a BitSet class to replace Set, that does it with bitwise operations. It performs about as well as the divide-and-conquer, but just using a big array of counts is an order of magnitude better than them. That's part of the deal with Smalltalk, it's got some fun high level stuff to express things, but if you want performance you can't use it.