Repository for my solutions to Advent of Code 2021.
I calculated the overall execution time from each part 1 and part 2 solutions (given my own inputs) alongside my time completion of puzzles (from 5am UTC start time) and my global rank for them:
Day | Part 1 Time | Part 2 Time (Accumulated) | Overall Execution Time (s) | Global Rank |
---|---|---|---|---|
Day 1 | 00:13:37 | 00:27:13 | 0.002 | 5960 |
Day 2 | 00:16:35 | 00:19:02 | 0.002 | 7843 |
Day 3 | 00:28:22 | 00:42:05 | 0.001 | 4294 |
Day 4 | 01:05:35 | 01:19:33 | 0.050 | 5906 |
Day 5 | 00:25:35 | 00:38:44 | 0.130 | 2995 |
Day 6 | 00:15:52 | 00:24:23 | 0.503 | 2685 |
Day 7 | 00:10:59 | 00:19:08 | 0.400 | 4856 |
Day 8 | 00:53:401 | 01:35:09 | 0.005 | 4498 |
Day 9 | 00:37:06 | 01:37:18 | 0.005 | 7399 |
Day 10 | 00:26:18 | 00:58:55 | 0.003 | 7274 |
Day 11 | 02:55:922 | 03:09:17 | 0.040 | 10232 |
Day 12 | 08:14:183 | 08:56:02 | 0.450 | 17522 |
Day 13 | 00:39:36 | 00:41:504 | 0.004 | 3420 |
Day 14 | 07:22:065 | >24h | 0.011 | 36937 |
Day 15 | 03:43:19 | 03:51:19 | 5.765 | 6729 |
Day 16 | 10:01:36 | 10:07:11 | 0.004 | 10935 |
Day 17 | 09:58:11 | 11:39:48 | 0.077 | 15199 |
Day 18 | >24h6 | >24h | 0.832 | 16901 |
Day 19 | >24h | >24h | 7.504 | 12519 |
Day 20 | 04:24:447 | 04:25:51 | 4.000 | 5884 |
Day 21 | 09:08:19 | >24h | 0.434 | 14314 |
Day 22 | 05:29:29 | 06:59:41 | 14.98 | 3755 |
Day 23 | 05:56:128 | 07:35:40 | 6.388 | 2754 |
Day 24 | 13:23:02 | 13:25:57 | 0.001 | 4263 |
Day 25 | 00:59:08 | 00:59:29 | 4.584 | 1836 |
My goal with these blueprint solutions is that I want to try and match them to the Python solutions I write first. If I find a better way of doing things, I update it in the Python solution first, and then update the blueprint solution. This is partially because I find it difficult to write BP if I don't have code to follow along with. Building upon this, it makes debugging the BP solution much easier since I can debug the Python solution as a reference. The only times this does not hold directly true is when I use a datastructure or library that does not exist in BP and I have to recreate with something my own, for example a defaultdict or Counter from collections.
BP cannot do file I/O on its own so I had to write a couple of small C++ functions to read in the input.
I then have a Blueprint Library with a function helper functions, including Read Input that calls upon the reflected C++ functions. I also have a pre-path set to the input text files directory so that in each new Day BP I only need to change the file name.
I then have a Base BP Actor that has a couple of variables and nodes already set up. I then can just duplicate the Base BP Actor and build my solutions from there.
There were a few different functions for various processes so I took screenshots of the most important ones. The first screenshot is the main function for parts 1 and 2. I wrote the BP code based off my Python solution.
This function below counts the number of 1s and 0s in each column and stores them into a struct.
This converts the binary string to a decimal number, since UE does not have a built in function for this.
The below function is effectively the main function for part 2, but since it gets called twice for the oxygen and C02 scrubbers I had to make it a separate function.
This function filters out the lines to determine the sole rating.
Day 4's input is so complex that I just did not want to spend the time to write a solution in BP.
Since parts 1 and 2 were very similar, I was able to put them into the same function with a parameter that checked which part was being run.
The overall program loop.
This parses the input into the array of structs where element 0 and 1 are integer arrays. This was basically my lists data structure you can see in my Python solution.
This function sets the x1, x2, y1, y2 values for part 1 plus the dx and dy values for part 2.
Then it does all checks for line intersections...
...and passes them into this function which adds them to a grid - which here is a struct where element 0 is an integer array (stores x, y coordinates) and element 0 is the count of intersections on each coordinate.
It also has to do an additional check to see if the coordinate already exists in the grid, which then increments the count at that coordinate.
Since parts 1 and 2 were very similar, I was able to put them into the same function with a parameter that checked which part was being run.
First I needed to parse the input into an array. This was actually more difficult than I thought it would be, for whatever reason.
The overall program loop that counts the fish. As you can see, I had to use int64 for the count because I was getting reaching the bit limit. Sometimes doing so much programming in BP breaks my brain and so it took me a solid 10 minutes to figure out how to convert from int64 to string, since UE4 does not have a built in cast for this, and obviously I cannot just convert to int to string as it would hit the bit limit again.
This function takes the starting ages and returns the list of fish at the start.
At first I thought I could just nick my parse input function from day 6, however I built it assuming that the numbers were all 1 digit, and since day 7 input as numbers of more than 1 digit, I could not use it. I know that I could modify it by 1) checking for the number of characters before the delimeter since last being split 2) add those chars to a string 3) converting to an int 4) adding to the int array. However, I couldn't really be bothered and decided to cheat a little by writng a C++ function to do it for me.
For my solution to work, I needed to sort the array. However, BP does not have a built in sort function, so I had to write my own. I decided to use insertion sort because although it's not the most efficient (O(n^2)), it's the easiest to write - I tried writing quick sort first but my implementation did not work and I couldn't figure out why.
I then just wrote the functions that I have in my Python solution.
Basically my part 2 solution.
I only managed to do Part 1 because I was unable to figure out how to do Part 2.
Before I discovered the Parse Into Array function that you can see in the above image, I tried writing my own first. I successfully wrote the function... but it used recursion, which although UE allows, I do NOT recommend. UE completely dies, as the initial function call has the first element become a call by reference, which although seems fine at first, it is not. Whilst debugging, the array passed by reference claims to have a value, but when I pass the array into a function, there is no value. So the function does not actually do anything. I asked a lot of UE4 developers and no one knew, but someone finally pointed me towards this Parse Into Array function that already exists, thankfully. So from now on I have know I am unable to implement recursive functions.
Day 9 was a bit of a copout, although slightly less so than Day 8. Part 1 was so easy that I programmed it all and it worked first time! But part 2, which involves recursion with depth first search, did not go so well. I tried giving it a second shot but got the same issue as in Day 8. So I decided to write part 2 in C++.
I had to parse the input lines into the grid first. Since 2D arrays do not exist, this had to be an array of structs of an array of ints.
Then it was just a bunch of if statements to check for the risk level of the grid.
For part 2 I basically just wrote a C++ version of my Python solution.
I kid you not, I was able to complete both parts first try (i.e. making them and working as intended instantly). Felt so good!
Part 1.
Part 2.
I had to write an Int64 version of my insertion sort function from Day 7.
This was really hard to debug! At one point I was getting an error that wouldn't crash the program, but rather put it into limbo - the program was trying to access some really high index numbers for an array of size 3. Turns out I wasn't creating the original grid correctly.
I'll show each step function first, then the two parts, as they use the same step functions.
Step 1.
Step 2.
Step 3.
Getting the adjacent tiles.
Part 1.
Part 2.
I haven't given up (edit - ok maybe I have)... just no time to work on these solutions as they are very complex :)
Footnotes
-
I woke up 35 minutes late. ↩
-
I woke up 2 hours 10 minutes late. ↩
-
I woke up 7 hours 30 minutes late. It was worth it though. It was the best night's sleep I've had since the 31st of November! ↩
-
I actually could tell what part 2 was going to be, so I predicted it before I submitted my part 1 solution, so that I could try and get like a 2 second delta. But like an absolute idiot I got the solution in the debugger output so when I needed it I was panicking because I couldn't find it and I had messed about in the code for a bit in between. So my delta ended up being just over 2 minutes... 🤦♂️ ↩
-
I woke up with a cold, which then lasted all the way up until Day 20. So I was prioritising sleep over this :) ↩
-
I "cheated" a little bit today by allowing myself to use permutations and reduce from itertools and functools respectively, but I'm still happy about it - I did this on Day 20! ↩
-
Yay finally! The first day since getting a cold that I could stay awake for long enough (I woke up at 8am) without my eyes watering or coughing my lungs out! Still kinda sick though, so I'm going straight back to sleep. Hopefully I'll feel even better tomorrow. ↩
-
I solved part 1 and 2 by hand first, then went on to write an actual solution for part 2 because, well, I was interested in how the problem should actually be solved. I should note that I did ask for tips on where to look for the coded solution first - because I honestly had no idea. ↩