Hello, there and welcome to my Advent of Code 2020 Day 17 cruise log. Today was not the day for being clever at 6 am. Sooooo not. I paid the leaderboard price for that. My worldwide rank isn’t so bad, for today I rank 3785 for the silver star and 3812 for the gold. Had I been more clever by not trying to be clever, my gold rank would be much better. Even the Advent of Code can hand you some life lessons, a surprise to be sure, but a welcome one.
Once more we start with the lore, our hero is back on a plane. The northern elves check in with us because they’re running some shady physics experiment and want our help. They’re playing with some multidimensional energy source and have no idea what they’re doing so we oblige and have a look. Now to the problem, it’s basically Conway’s game of life but this time within a cube. Lucky for me, unlike the last time we dealt with Conway’s game kind of stuff, I have no piercing headache.
Our puzzle input represents an initial state for that energy source which has some active and inactive cubes within it. Also, any cube beyond that initial state we get is inactive. The first part consists of counting the active cubes we have within our energy source after running six cycles. During a cycle, we apply rules to determine whether a cube becomes active or inactive. For part one we need to return how many cubes are active after running six cycles.
I start by creating a cube entity which I begin with a method to check if a given cube is another cube’s neighbour. Next, I create a test for that and it unfortunately it passes. Well, it should be passing as the data matches the example expectations. But there is a bug, one that will waste me a bunch of time later.
Now that I have my cube struct, I create my pocket dimension structure which represents the plane where the energy source resides. The first method I create, through test-driven development, of course, is one to create that pocket dimension from the puzzle input. The test for it loads a pocket dimension and counts how many active cubes are in it. I’m expecting five and do get five. So far, so good, but there is still a bug lurking, waiting for the optimal time to ruin my morning.
At this point, I have most of what I need to complete this first part, except for the method that counts the active neighbours for a given cube. I implement the test first, which loads a pocket dimension then iterates over a few points which each should have a certain amount of neighbours and I validate these. The first point test yields the right result but the second returns much more than it should. It actually returns seven when the whole dimension contains five.
I scratch my head, go through my counting method which is fairly simple and it seems correct. Next, I check the method to load the neighbours of a cube. Looks fine. Then, I go through the method to load the vectors to apply in order to create a cube’s neighbour but nothing. No pen and paper this time, I have the funny feeling they won’t help me debug a three-dimensional array. However, command-line debugging will. I print values like crazy and realise that some neighbours checked should not be returned by the neighbours’ retrieval method. I add a test to count that I have exactly 26 neighbours (3^3 – current one) and it returns 52. Exactly twice the size I should get.
This is where I realise that I initialise my map using the length of the neighbour vectors array. This naughty initialisation taunts me, after hiding in plain sight for so long. Since I’m still quite newb in Go so it could be the issue. I decide to initialise it empty with no size. And the size check test passes but the neighbours count still doesn’t, that count didn’t change one bit. The test still fails, shame and despair start creeping in, is it the day where I give up? I wish upon the stars to get one more day, one more day in top spots of my company leaderboard, one more day claiming stars, one more day writing a daily log with a happy ending. People love happy endings.
From there I know that I need more tests around the neighbours’ retrieval because some dodgy stuff is going on there. But before I do so, I realise that some neighbours are duplicates and if I count without these I get the right value. At that point, I add a test to count the deduplicates neighbours and expect to get 26 but get 17. Nine neighbours are missing, a whole dimensional slice. I must have messed up the neighbours’ vectors method. Literally, the first thing I wrote today is what is failing me. Damn you 5 am code!
Upon that realisation, I recreate the neighbour vector method from scratch which really just returns a hardcoded collection of all the possible vectors. Taking my time, meticulously setting each value, x after y after z. Line by line, dimension by dimension. I run the neighbours counting test and it passes. Now I can run the part one test, it passes and I run my input against the code and it freaking passes! The first star of the day check, I’m pumped up, I feel unstoppable and then I see part two.
Part two the filthy elves tell you that your simulation results don’t match what they’re seeing and they made a mistake. They’re not dealing with three-dimensional entities known as cubes but four-dimensional entities named hypercubes. Now you need to get the eff outta here! So much code to rewrite but I like to think I am a clean coder and there must be a clever way to refactor part one to make it work here. So I try that, I create an entity interface that has a getNeighbours method alongside a isNeighbour method. From there I would only need to adjust my methods to handle either cubes or hypercubes.
All hell broke loose, most tests failed and since I don’t commit my code until after writing this post, yes huge mistake, I had to manually revert everything without losing my bug fixes. After some painstaking thirty minutes of back and forth, I copy-paste part one into a second file. There, I apply some search and replace from cube to hypercube and from pocketDimension to pocketHyperDimension, not so creative. Then I add a fourth dimension to my now hypercube and update the neighbours’ related methods to take it into consideration.
Now I can’t be bothered adding tests for these so I only write the part two example test, it’s almost 7 am. It passes on the first go, next to the input runs in a few milliseconds and it grants me my 34th star this season. If only I did the biggest copy-paste in the history of copy-paste right away. I could have been done ions ago, shame on me. Being clever has never been such a stupid choice but at least I can learn from that, and hopefully, so can you.
Thank you for reading my Advent of Code 2020 Day 17 cruise log, I will see you tomorrow, maybe. In the meantime, you can check out my Go Cloud series about building and deploying a Golang app to AWS. Also, I will push my code on Github at CodingNagger/advent-of-code-2020. Feel free to check it out but not before you’ve done the challenge yourself. Bye!
Cover by Evgeny Tchebotarev from Pexels