To get the most use out of Stack Overflow you need to reach escape velocity… meaning you need to be able to know what question you are actually asking and evaluate which of three upvoted answers is the one you need. I have used it a lot.
But it only solves technical issues like libraries and interfaces problems, maybe algorithms or certain language concepts.
You still need to be able to design the thing and know the process for ending up with quality software.
Like the other day when I didn’t receive a package because a part of my address was eaten by the shop software when a backend was trying to fit the address into a database format. Someone writing code for a pretty big shop system apparently had no idea how to evaluate which parts of their application needed how much test coverage… or how to test properly.
The difficult thing is making it efficient and robust and extendable and making sure it is doing what it is supposed to be doing. Figuring out the latter is a whole different rant…
Hi, I work in software quality control. I specialise in finding ways to make software do things it isn’t supposed to, and then making it the devs’ problem
I started coding back in my penultimate year of high-school writing Basic on a hand-made micro-controller. I then moved onto Python a year or so later.
At uni I did a bit of Ada, C, C++, Prolog and Scheme as part of my CompSci and Mathematics degree.
I graduated in the recession of 2008/2009 but I’ve now spent a decade working in aerospace as a software engineer.
For the vast majority of that decade I’ve been working for a company specialising in Electronic Warfare systems. I’ve written software in Ada, C++, C#, Delphi, MATLAB, Python and VBA (shudder), some of which has made its way onto the Panavia Tornado and the Eurofighter Typhoon. My current work will eventually end up on the next weather-themed aircraft.
A lot of my time recently has been spent doing Scrum Master stuff or managing our DevOps stuff, but I do still find time to get stuck into code.
Away from work I have too many Raspberry Pis to count and I like doing projects that involve LEDs. You might have seen a few in the maker thread. This year has been a bit quiet on that front though.
I’ve been keeping up with the Advent of Code. Day 13 felt very gatekeepery to me as someone who never had a chance to dig deeper into some of the more obscure areas of number theory (though, perhaps I should have encountered it during my computer security studies as well), but first a warning: Spoilers ahead!
I’ll also say that Day 13 is the only challenge that I had to check the subreddit before solving part2.
Day 13, part 2
The common chatter for Day 13 on the adventofcode subreddit was to solve Day 13, part 2 via the very straightfoward† approach of Chinese Remainder Theorem… so I did without really understanding other than making all the bits fit together.
My naïve approach that I used to solve part 1 is still running to solve part 2 after over a day and a half, mostly just as a curiosity at this point… I really wish I had prefixed it with the ‘time’ command.
But… it bothered me that I shoehorned in a couple of pre-written functions I found after being lead to Google search for “Chinese remainder theorem”. At the back of my mind, I was convinced I could optimize my original solution to just take smarter steps (rather than the largest of the next valid timestamps of any of the routes). So I finally got around to doing just that… working out a smarter stepping mechanism. And… it worked on my first try! What? I actually still don’t know if it’s a fluke or what.
Day 13, part 2, LCM stepping
import argparse
import sys
import re
import math
import numpy as np
class BusRoute:
def __init__(self, bus_id, seq_id):
self.bus_id = int(bus_id)
self.seq_id = int(seq_id)
def valid_timestamp(self, timestamp):
if (self.seq_id + timestamp) % self.bus_id == 0:
return True
return False
def next_after(self, time):
return math.ceil((time + self.seq_id) / self.bus_id) * self.bus_id - self.seq_id
def main(args):
bus_routes_spec = [ line for line in args.buses ][1]
bus_routes = list()
for i, b in enumerate(bus_routes_spec.split(',')):
if b == 'x':
continue
bus_routes.append(BusRoute(int(b), i))
timestamp = 0
step = max([ bus.bus_id for bus in bus_routes ])
len_bus_routes = len(bus_routes)
while True:
valid_routes = [ bus.bus_id for bus in bus_routes if bus.valid_timestamp(timestamp) ]
if len(valid_routes) == len_bus_routes:
return timestamp
if len(valid_routes) > 1:
a = valid_routes[0]
for b in valid_routes[1:]:
a = np.lcm(a,b)
step = a
print(f"# DEBUG: new step: {step}, timestamp: {timestamp}")
timestamp += step
if __name__ == '__main__':
parser = argparse.ArgumentParser(prog='day12-1', description='Determine Bus Offset Departures')
parser.add_argument('buses', nargs='?', type=argparse.FileType('r'), default=sys.stdin)
args = parser.parse_args()
print(main(args))
†: straightforward if you both understand CRT and how to break apart the problem to properly formulate the solution.
Well, I wasn’t familiar with CRT, but I could see the combinatorial explosion coming (many of last year’s problems took the form “do this simple thing and get the result of iteration 20” followed by “now get the result of iteration eleventy billion and three”). So I was looking up modular arithmetic on Wikipedia and that took me to CRT, at which point (a) I read the bit that said “nobody does it by stepping” and (b) I poked CPAN and found I had a module which would do it already installed.
If I’m reading your code correctly, your step is the lcm of all the routes that were correct (and they should therefore all remain correct at the next step). Makes sense to me.
I try not to look at the Reddit thread before solving unless I am completely stuck.
day 15 part 2
This one felt like bait for people going for the scoreboards (which, since the problems come out at about 5am my time, I’m not doing). 3×10⁷ is only about 1.5×10⁴ times larger than 2020, so if my part-A code ran in 0.015s (which it did) the same code should solve part B in ~200s (less, because that includes startup time). Which is probably faster than I can write something cleverer, so I’ll at least fire it off while I’m looking at the pattern of results and thinking about the problem
Yeah, when I got to part 2, I thought to myself “oh great, another day 13 part 2”, thinking it was going to be a O-notation measuring contest like d13p2 was… But my naïve solution would have solved it in about 3 minutes. Still, in the spirit of the challenge, I refactored, lost the full history/retention, and just memo’d the most recent position of all terms seen
Honestly, I was a lot happier with how they presented d15p2 as a challenge than I was with d13p2
Yep, exactly. Compute the least common multiple of all the buses that were valid for the current timestamp and use that as the new timestamp step.
I was so flabbergasted that it worked I didn’t even optimize to not recalculate the same set of valid routes each time (theoretically, the ones I’ve already optimized for will always be valid now, meaning it’s just keep recalculating the same thing until another route is valid.
Day 16 part 2 marks the first challenge where I just couldn’t brain enough to complete before my brain shut down around midnight:30 (about an hour and a half after it was released).
I think the problem was that I saw the part 2 challenge coming but I ended up getting bogged down in a slight misunderstanding of part 1 (I was counted each instance only once, rather than every time it occurred… I interpreted the challenge 1 way, it was intended the other way… and the language is still vague and, if you ask me, they should have included an example that used a duplicate invalid number) and my subconscious brain was, all the while, silently panicking about how to solve for the inevitable part 2.
So, by the time I read the part 2 challenge, I was already convinced I didn’t have the brainpower required for the solution.
This morning, after taking my kids shopping for some Christmas gifts for my partner, I sat down and typed out a crude-yet-effective solution to part 2 without hardly having to think about it at all (probably because my subconscious spent all night mulling it over)
I rather liked 16; I didn’t see the ambiguity you did so I just steamed on ahead with it. Part 2 reminded me of Sudoku-solving code I’ve written, but fortunately in both the test data and my live set I only needed to write a single solver-rule
Huh, spoilers aren't working
(if there’s only one field that can match key K, remove the other keys from that field’s listing)
that could be applied repeatedly. (I assume this was deliberate.)
17 caused me to groan a bit because I am, as Johnson didn’t put it, tired of Life. I was expecting part 2 to be "now do iteration $MANY", but as it turned out it only needed a minor tweak to my existing code.
Day 17 is the first time I didn’t solve the challenge before going to bed. Mostly because I was stubborn and refused to back down and take a different approach. I’m sure now that I have slept, it’ll come along a bit easier
Yeah, it turns out my implementation was fine. This was a good example of why you shouldn’t write important code late at night; I fell into the classic if test1 or test2 and test3 snare… it should have been if (test1 or test2) and (test3)
But, while toiling away, oblivious to that particular problem, I did some optimization (still too stubborn to give up on my initial approach… which was justified in the end).
And then Part 2 was a bit of s/(x,y,z)/(x,y,z,w)/ and then being thankful that my initial approach utilized sparse data mapping with aggressive culling
I actually built the whole thing as a 3-dimensional array (with an offset based on a maximum spreading speed of one per generation; but since I needed to keep track of the total, instead of doing the standard thing of initialising two arrays then processing A into B, I built a changelist (if n==3 and current==0 set to 1 and add 1 to total, if n!=2 and n!=3 and current==1 set to 0 and subtract 1 from total), then implemented that changelist in a separate pass.
Not the quickest, but even the four-dimensional version (in Perl) only took about 20 seconds to run. I think I developed this approach last year, of running the simple model while I started thinking about how to implement the more complex one, on the off-chance that it would finish first.
I opted to track active nodes in a dictionary based on their address hash (using a (x,y,z) and (x,y,z,w) tuple in Python as the dict key). With some wrappers around the dict to automatically generate inactive nodes at coordinates that were requested but did not exist in the dict.
And then for each generation, I would create a new dict of nodes that survived or were born and then just swap out the old dict with the new.
I’ve been looking at the Advent of Code today - having basically finished real work for the year. It’s fun, but I’m going to have to bail at just 13 stars. They are getting quite time-consuming now, and I sense that by the end the challenges will take ages to write/debug - if I can do them at all. Lots of the challenges are the sort of text manipulation I’m quite good at, but trying to write code in perl is always a challenge, because I really struggle to do things (which I feel should be simple) like make a hash containing arrays, and reference the elements in the right context. I don’t know whether I’ll be lured back later.
That seemed rather lighter. Basically “write an expression parser, and to make sure you don’t just use eval or equivalent we’ll change the rules”. A three-step for me working on a string: recurse to turn parens into simple numbers, evaluate high-priority ops, evaluate low-priority ops.
I’ve been faced with this type of challenge before and never had a good approach (parsing order-of-operations expressions). This time for part one, I just iterated through the string to convert the terms into python lists: [ ‘1’, ‘+’, ‘2’, ‘+’, ‘3’ ], with parenthesis being added as a sublist; [ ‘1’, ‘+’, [ ‘2’, ‘+’, ‘3’ ] ]. And then it was just a matter of iterating through, performing the operations and, if necessary, first resolving a sublist if encountered.
I totally expected Part 2 to be, “Okay, now do it with ^, /, - all with a custom order!” So I was pretty pleased with the actual Part 2, even though at first I was filled with dread. I realized that instead of rewriting everything I had done, I could just pre-parse the expression and add parentheses around the terms on either side of the ‘+’.
And in yet another surprise, it worked on my first SWAG, which really confused me because I had put in (a bad habit, to be sure) a bunch of print statements to track what all happened during the evaluation… and it ran much longer than I expected, so while my screen blurred in scrolling text for a second or two, I was thinking, “oh great, I made an infinite loop”… but a moment later it spat out a rather large number, copy-paste-submit revealed that it was right.
I’m always surprised when I write code correctly the first time.
I’m not even going to look up that Advent of Code – I can tell already that it’s going to be another time-sink I don’t need : )
I program for work and also in small doses as a hobby, but I keep the two as separate as possible. My hobby programming is mostly in Emacs Lisp. I started out just hacking on my own Emacs config; and from there to submitting bug reports and patches upstream to Emacs and third-party libraries; and then writing a few small libraries of my own; and now the most recent release of Emacs has one of my libraries in it as standard, so that’s neat. My laundry list always grows faster than I can get through it (which I’m sure many of you will relate to), but a few of the projects get out there from time to time. Finishing things is hard, though – as the adage goes, once you’re 90% done, all that’s left is the remaining 90%. I really spend more time contributing to that community by answering Stack Exchange questions than I do by writing code, though; and it’s a pretty great community in general. The core maintainers are absolutely amazing – it’s a 100% volunteer spare-time gig for all of them, and the work they put in is incredible. There’s so much going on though – it’s utterly impossible for me to keep up with all the new developments (or to even come close to doing that), but I love being involved in a piece of software which fosters that kind of creativity. I also love that the text editor I started using more than 25 years ago is still my preferred text editor – in a field which changes so rapidly, it’s nice to have these kinds of constants.