#2023 AoC Day 11
133 messages Β· Page 1 of 1 (latest)
oh god please i hope not ||travelling salesman|| in part 2
||it's not travelling salesman||
OFF BY ONE! OFF BY ONE!
well
I tried to ||way overcompliate it at first||
but then I ||did the simple thing||; rank 757
I'm guessing you did what I also almost did ||and not immediately realise that "make it one million times larger" actually only means "add 999999 blank rows", not "add a million blank rows"?||
that is exactly what i did
I checked that one against the example first
key to success here is ||not actually expanding the universe||, which i suspected might come up in part 2 if ||it wasn't travelling salesman||
||Expanding the universe was just fine for me
||
|| Surely the day after the problem is hella tedious because I didn't adopt the cornball strategy of mapping it to a larger grid, they aren't going to have a problem where part 2 penalizes you for just mapping it to a larger grid, surely... ||
|| π ||
|| (and don't call them Shirley) ||
also found that ||adding rows but removing columns was easier when constructing my empty sets, which was fun||
Boo yeah I shouldn't have figured out how to do the thing I did in P1
Mine is pretty similar to everyone else's... ||https://github.com/mrphlip/aoc/blob/master/2023/11.py||
just realised i could ||cut out the work completely in part 2 because of how i modified part 1 trying to debug||
This almost got me too, FFS.
That sense of crushing despair when "This seems like a simple modification" turns into "My answer does not match example anymore and I don't know why"
I was confused by making that exact same mistake for about 15 seconds, then I re-read the problem and went "whoops"
Sigh... I don't want to do this...
we will think no less of you if you choose to not
This one isn't too bad, though. It's easier than yesterday!
also, I got to use one of my favorite Haskell functions:
||```haskell
pairsOf :: [a] -> [(a, a)]
pairsOf ls = [ (x, y) | (x:xs) <- tails ls, y <- xs ]
It's just such a silly thing to do.
my brain is already thinking... but since I can see spoiler text, I know what Part 2 is, so I'm going to have a big think before I even begin to come up with other approaches...
amusingly, the harddest part of this is, given R, working out the empty rows/columns...
Aw man, I can't even give you spoilered hints in case you want them, because that doesn't do anything.
well, I just lost ~20 minutes to a stupid issue:
||a 12 digit number isn't gonna fit into an int ya dummy, of course it's just gonna give you random nonsense||
also, rather than ||expanding the grid, I just noted which spaces were empty, and added 1000000 to the path if I hit them, instead of the 1 for a nonempty space. this did make it easy to eventually figure out the right solution by counting how many times I "crossed the empty", at that point the solution it becomes even easier: base distance + crossings * (expansion factor - 1)||
today was pretty straightforward, though I was worried what part 2 was going to be. sadly, did not wake up early enough for leaderboards (but it was early enough that I can nap for an hour before getting ready for work now :) )
Done it
Since I knew what was up, I had time to plan, and so I think I got quite neat code
I ||got the coords of each galaxy, worked out where the gaps where, then inflated the coords by each gap in between, then a simple n^2 step to sum distances||
...and that was a Sunday puzzle!
It may not scale difficulty exactly like the NYT crossword
Quite nice if I do say so myself [language R]
it is common wisdom (aka some guy down the pub said) that weekends are harder
this is a monday puzzle btw
I spent longer on part 1 than part 2 because ||expanding the universe|| actually ended up more finnicky than just ||counting crossings||
unfortunately largely not supported by actual data
I'm not convinced day 5 was actually that hard
It was hard if you didn't do it the right way because part 2 value set was extremely large
pfft, data, what did that ever get you?
yeah, one of those unwritten AoC rules ||actually expanding anything is usually a mistake, there's probably maths for that||
Data once saved me from a crystalline entity.
22nd of 2019 is certainly burnt into my memory as hard
||You can't trick me with your fancy diagonal paths, I know a Manhattan distance when I see one!||
|| π ||
|| I'll never forget Manhattan metric|| because we spent almost entire semester using it as an example for Topology 101
L1 and L2 get used all the time. When do we get a problem based on L3?
Give me Lβ!
I think if that's defined, all distances will be either 0 or 1
L inf is basically max.
Oh, I see
The diagonal seems weird, though
Eh, I guess the root of 2 approaches 1, so it's fine also
|| In part 1 I parsed the input directly to a list of vector positions which meant the "expanding" was not an issue except for the fact I didn't have a 2d vector class that used longs, fixed that now. Worked around it originally.||
Hello yes it's me joining the off-by-one crew for part 2
OK, I think I probably did today's in a standard way. Good Monday-morning problem, I think.
Part 1: Naive. Though I initially thought ||I was supposed to sum the distances to the closest galaxies, not all galaxies.|| Thankfully I noticed the problem quickly because my example output was way too small.
Part2: ||Store a list of offsets for X and Y values, add them while converting the base representation into a coordinate system.|| I tried the solution to part 1 first, because I wasn't sure whether Python strings would die on these inputs or not. Turns out they do.
Python numbers are so nice, I wasn't sure if Python strings had some hidden niceties too like they were secretly ropes or something that can better handle the stupid operations I was doing to them.
I need to learn not to try and solve this at work. It's like an ADHD simulator with people bothering me every 2 minutes.
the fact that python has arbitrary precision integers helps out a ton in a lot of AoC puzzles
I try to solve it while having my morning coffee. If I finish my coffee, it's time for real work and I can pick it up again at lunch.
Yeah, the numbers thing is why I use Python even though I hate Python more every year I do AOC.
Every year I learn new ways that Python does not do what I expect it to do.
This year it's how lambdas and closures work (or rather, how they don't seem to work).
python's lambdas are notoriously bad, they were intentionally designed to be bad because the BDFL didn't like functional programming
But thankfully it has partial functions which do work the way I expect them to work.
what was it about closures that didn't work how you expect?
closing over a loop variable?
Let me see... There was a day where we had to pass numbers through a bunch of maps, for seeds or something? And I wanted my maps to be closures over the values in that instance of the loop. Something like this:
for x in (..):
someFunction = lambda y: x + y
someStructure[key] = (..., someFunction)
...
result = someStructure[key][-1](value)
But that didn't work. The variable x wasn't correctly captured in the lambda's definition. I ended up currying another function with the partial:
def someFunction(x, y):
return x+y
for x in (..):
someClosure = partial(someFunction, x)
...
result = someStructure[key][-1](value)
And that worked fine.
yep, that's a footgun a lot of people hit
not unique to python either, golang has the same issue as do many other languages
I'm not entirely sure how Python binds local variables to lambdas, but it's not what I expected.
the closure has a reference to the outer scope, and variables are looked up in that outer scope
so x is "whatever the value of x in the parent scope is right now"
Oh, is that all it is? But x is defined only in the loop? How can it even be expected to work this way??
well technically x isn't only defined in the loop, when the loop exits x retains the last value it had while looping
but if you say, explicitly deleted x, you'd get a NameError (no such variable) if you tried to use it inside the closure later
It is amazing how bad a language can be and still be this popular.
(In before Java jokes)
for example:
>>> def f():
... return lambda y: x + y
...
>>> g = f()
>>> g(0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in <lambda>
NameError: name 'x' is not defined
note that the name error doesn't occur until the lambda is executed
these are not mutually exclusive (this I say, as I chose to solve AoC this year in Java/Groovy)
just to drive home the point that this is a dynamic lookup, watch what happens when i define a new global x, after the fact:
>>> x = 1
>>> g(0)
1
all variable lookups in python search all containing scopes in order until the first scope where the variable is set.
normally that's just local scope and global scope. In a closure, it's local scope + the parent scope + global scope
Would this behaviour also work this way if I'd used a local def instead of a lambda?
yes
OK, then I'm really glad I looked to currying as my next step, because I'd considered using def briefly to see if that made a difference.
def foo(): return bar is (mostly) identical to foo = lambda: bar
the (mostly) comes down to things like "does the function have a human-readable name in a traceback"
Makes sense. Thank you for enlightening me into this nonsense.
Funnily enough, Java avoids this exact problem by requiring all captured variables to be final (or effectively final). Which can be cumbersome in its own ways, but avoids this particular footgun.
The closure-in-a-loop footgun exists in some form in pretty much every language that has both closures and mutable variables. Python, Javascript, Go... it's a common problem.
C++ of course takes the kitchen sink approach and lets you specify whether a lambda captures by value or by reference.
Some languages manage to avoid it in various ways... Java requires all captured variables to be final to sidestep the issue. C++, in typical fashion, lets you explicitly specify if variables are captured by copy or by reference. Javascript has let keyword which changes the loop semantics.
Ruby has loops be usually implemented as function calls already, so each loop body is its own entire scope
That's a useful amount of control for C++ though
This was my problem at first (thankfully not for long); ||I started going like, "oh, I need some big, fancy algorithm for this, don't I?" but thankfully I didn't look at that for too long before I looked back at the problem and said, "wait, this is just Manhattan!" And Manhattan distance is easy.||
I wrote a nice quick solution to part 2, then spent an hour manually verifying and debugging only to realize I was modifying the input data in a way that created a repeating off by one error π
Also, doesn't understand functional programming.
[...] makes it possible to easily implement concepts that superficially resemble concepts from functional languages, like currying, map, and reduce. The primitive operations that are necessary to implement those concepts are built in Python, where in functional languages, those concepts are the primitive operations. You can write reduce() in a few lines of Python. Not so in a functional language.
like... what? he thinks these things are primitives in functional programming?
Human kind has many ways of describing Hell. Flaming tar pits, loneliness, other people, the island from Lost, the It's a Small World After All ride.
To me, Hell is explaining how my Excel sheet for Day 11 works.
And/or my thought process in tackling this problem.
my spreadsheet is fairly nice. this one's fairly gentle on excel
I've certainly made a few decisions and my ass is very much bitten.
well it's not that gentle if the choice you made for part 1 was ||oh? there's only 6 empty columns? i'll manually inflate. but obviously that's not viable for part 2. but it's not too bad to just add penalties to the coordinates||
It's 100% what I did
i mean it's what i did too
I'm happy with my decision to continue with Excel and not converting my new input to LUA because that would have been a nightmare.
anyway on first reading i thought the problem was find ||the pairing between the galaxies with minimal sum of distances and that's a much much much harder problem lmao||
Solved.
The problem itself is not that difficult ||abs(x1-x2)+abc(y1-y2)|| It's just arranging all the ducks in a row.
Once again, a crime has been solved using math,
Like all crime, it was also committed by math.
well, this would have been a significantly more unpleasant problem in excel with a different metric
Missed a bunch of days due to life stuff, but had a chance to get back to today: Part 1 ||I did the naΓ―ve thing and just expanded the array, which turned out to be a mistake when I got to part 2||
did you also fall into the other trap? ||which is adding 1000000 instead of 999999||?
Part 2: ||Refactoring it for part 2 made things a lot simpler and faster, though slightly more edge case management with the list of arrays||
Are you doing this in like...verilog?
it's LabVIEW, but basically yes
do you mind if I share this with friends in a different discord?
because this is wild and cool
One of my friends joked that the spoiler warning isn't necessary, no one can read it anyway. (Another friend just complained about how painful LabVIEW is for complex stuff, despite being easy to get started with...)
it really bogs down for complex math, but it works well for control systems