Link for the same article in my blog here.

From the website, Advent of Code is an Advent calendar of small programming puzzles for a variety of skill sets and skill levels that can be solved in any programming language. People use these puzzles as a speed contest, interview preparation, company training, university coursework, practice problems or to challenge each other.

This was the first year I decided to participate, not for any competition (the puzzles unlock at 5AM in my time zone), but to challenge myself and see how my programming skills were. Just to have a reference, I have 3 years of professional experience with .NET Core/C# and took a master’s degree on Computer Science. I tried to manage it with my daily job in a way that I would try not to lose much time in case I got stuck at something. So, I reserved 2 hours each day for it.

In this article I will describe how I approached each puzzle and how I solved them. I also provide a link to my GitHub for the solutions I made. Feel free to criticize! That is the way I will get better! I am no genius! I’m sure there are better solutions out there, but these were the ones I got.

Day 1 — The Tyranny of Rocket Equation

Day 1 sounded straightforward to me.

For part 1, I just coded the math needed to calculate the fuel needed to each module according to the formula on the puzzle description.

Part 2, more of the same: calculate the fuel needed according to the description.

Ok, this looks easy!

Day 2–1202 Program Alarm

At Day 2, it was the first day IntCode programs appear. And how many times they appeared again!

This program we needed to build, at Day 2, needed to support operation codes (opcodes), which would indicate the operation to be done to subsequent integers in the list.

The opcodes to be supported in this day were adding two numbers from two positions and store the result in a third position of the list, same thing with multiplication, and another opcode to indicate when the program should finish.

For part 1 I started implementing a method to support these opcodes with a beautiful switch statement (oh the pain I will get because of this decision). Replaced the two given values for the first positions of the list and… It worked quite well!

At the start of part 2, I see the following sentence: “Good, the new computer seems to be working correctly! Keep it nearby during this mission — you’ll probably use it again. Real Intcode computers support many more features than your new one, but we’ll let you know what they are as you need them”. Hmm, not the last time I will see this…

At this part we needed to calculate what pair of numbers for the first two positions of the integers list produced a specific output. We knew that both were between 0 and 99 so, using two for loops was the decision I made to calculate this. A little brute force but… It worked!

Day 3 — Crossed Wires

Day 3 description at first it seemed easy for me when I first read it. But then it revealed as more complicated than it seemed! Ah these wires!

The goal of the part 1 was to find the Manhattan distance between a central point and the closest intersection point where two wires crossed each other. I started doing this with a matrix. Yes, a matrix. I gave up after some time as I see it was not going in a good direction. Switched to register all points where both wires passed in two lists. Then used the Intersection method from Enumerable to get the points were both wires intersected. Calculated all the distances to the central point and selected the lowest one. Part 1 solved!

At part 2, instead of the Manhattan distance, we needed to calculate what sum of both wire steps was the lowest. Made a very similar approach to Part 1 and calculated it.

On to day 4!

Day 4 — Secure Container

Day 4 was one of the easiest days for me.

Based on different criteria for part 1 and 2, the goal was to calculate how many different passwords between a range of numbers would match such criteria.

While loop between such numbers, apply criteria, count if criteria applies in each case and both parts done!

Day 5 — Sunny with a Chance of Asteroids

Day 5 marked the comeback of IntCode programs. They are here to stay!

For part 1 we needed to add two new opcodes to our IntCode program. Input and output operations. It also introduced the notion of parameter modes: if a value is in a position or if it is the “real” value. To add more complexity, such parameter modes were stored in the same value as the instruction opcode. Also, the instruction pointer would not increase by 4 anymore but by the number of values in the instruction.

For new opcodes and to support parameter modes, I started expanding my switch statement to support this. It started to look ugly but manage to finish the job for part 1.

For part 2… FOUR NEW OPCODES. Jump-if-true and Jump-if-false (both set the instruction pointer), less than and equals. ALL of them need to support both parameter modes.

And here I go, expanding even more my switch statement until it reaches almost 140 lines. What a powerhouse!

At least it got the job done… I hope this is the last time I need to make changes to this IntCode program…

Day 6 — Universal Orbit Map

Day 6. Orbits! Yes, orbits!

For part 1, the goal was to calculate the total number of direct and indirect orbits given a provided input. To make this I inserted all orbit pairs in a list and used the Depth First Search (DFS) algorithm to calculate such orbits. Done!

I have spent so much time in part 1 just to implement a DFS that I postponed part 2 for the weekend where I would have more time to solve it. Part 2 of Day 6 was made with one day delayed.

The goal for part 2 was to calculate the minimum orbital transfers required to move from one object that is orbiting to another. And here I spent so MUCH TIME just thinking! I could not manage a solution for a long time.

But then, a Eureka moment came and the solution: if I calculate the distance from both objects to the closest intersection point, I have the minimum orbital transfers needed! It seemed so easy after thinking about this. I filled a sort of graph in the form of a list to know the orbiting objects around each object. Then calculated the distances for each object from the two objects, intersected both lists, summed and checked the lowest value.

Done, finally!

Day 7 — Amplification Circuit

After solving the part 2 of Day 6, I started tackling Day 7 on the same day. IntCode programs again!

Part 1 had the goal of having a set of amplifiers and to try every possible combination of phase settings in each one of the amplifiers and find the largest output signal that can be sent through the amplifiers, the output of the final amplifier. Oh boy!

I had no good support for Input and Output signals in my IntCode Program, so I continued to try to work on my behemoth. No success! I had a bug and spent so much time trying to debug it (1 day) that Day 8 had came out and I was still trying to debug day 7 puzzle.

I just had given up debugging and made the best decision that I could make (now that I look to the general picture, it really was!). I completely refactored the IntCode program, created a class for it and to avoid a gigantic switch statement to detect which opcodes were given, I used the Strategy design pattern, alongside a dictionary to make the mappings between the opcodes and the concrete implementations of the “strategies”. Plus, started using queues for Input and Output signals.

Good! The code looked much cleaner and the debugging was much easier and guess what… Bug in the input opcode! Fixed and part 1 done!

Part 2 was straightforward after this refactoring. Just needed to have a good way to provide the feedback loop through the amplifiers with the Input and Output queues I created.

Done and I still must solve Day 8 “today”, Sunday! Can’t leave this to workdays because of my daily work! Also made unit tests for every day done to make sure everything was working. Mainly the IntCode program!

Day 8 — Space Image Format

For Day 8, images and layers!

Part 1 was just to make sure that the image was not corrupted. For that, we needed to check which was the layer with the fewest 0 digits. We knew the image dimensions… Fine! Used a List of lists (now that I look at it, not the best idea maybe…). Made use of the Take method from LINQ and added each layer as a List inside of a List. Next, made use of the Count method all the way, multiplied the number of 1’s by the number of 2’s and done! Nice!

Part 2! Decode the image! What? How am I supposed to do that?! The description of this part form me sounded clear but I had no idea how to “decode it” (it was late Sunday and I was tired — amazing excuse).

And then I remembered that I could just print everything in the console and then see the “message produced after decoding my image”. 0 is black and 1 is white. Then “#” symbol for 1 and “ ” (nothing) for black. New line after every 25 digits. Print in the console. And there it was! Done at time for the beginning of a work week! And added boilerplate code and unit tests for all the remaining days! Mission accomplished!

Day 9 — Sensor Boost

Day 9 arrives. IntCode! Please, everything is refactored and cleaned now, this should become easier! Please!

At part 1, a new mode needed to be added to the IntCode program: relative mode. Relative base mode counts from a value called relative base that needs to be stored. Every time it appears, relative mode parameter refers to is itself plus the current relative base. Cool! More to add: an opcode to adjust such relative base value, support to “much larger numbers” and “computer’s available memory should be much larger that the initial program”.

To tackle the relative base, created a property at the IntCode program for it and needed to change most of the concrete strategies to support it (had a bug in input opcode because I had forgot to support this new mode in it — spent some time on debugging) and made a new one to support the new opcode. For large numbers, instead of int (Int32), replaced everywhere in the IntCode program for long (Int64) types. To support the described “larger memory”, every time the instruction pointer had a value bigger that the size of the input, resized the array using the Resize method from Array library.

Time consuming but relatively easy. If I had the switch statement on the IntCode Program still, would have been much worse. Part 1 done. Organized code and design patterns make wonders!

Part 2 just needed to provide an input value to the Input queue. Done!

Day 10 — Monitoring Station

At day 10 we needed to make some asteroid detection at part 1 and asteroid vaporization at part 2. Needed to make high use of Mathematics at this one.

First, given an input we need to know where is the best place to setup an asteroid detection station. For this, I registered all the positions of the asteroids in a List of KeyValuePairs first. Next, to know the best asteroid to detect the highest number of asteroids, iterated through the list and made use of vectors to know the ones that were in direct line of sight. Described like this seems easy! But to conclude that I needed vectors took a while. Plus, I had one “major” error in the logic: when calculating the vector between two asteroids (or points, in a more abstract way), if the target asteroid had some blocking it, it was not being detected. Solution? Had to calculate the greatest common divisor for the vector and divide it by such number in order to get every asteroid in that line of sight.

Regarding part 2… Oh the headaches! At the beginning I had no idea what to do. I mean… I had but did not know how to do it. Understand? Never mind! In this state of mind, I reached the Reddit community for Advent of Code searching for suggestions. Trigonometric!

I had to make some quick recap of what I knew about the topic and the idea of how to do this part came immediately to my mind: make use of angles and the trigonometric circle. Although the logic was the right one, I had some problem coding it. Until I discovered that there was the Math.Atan2 method which calculates the angles whose tangent is the quotient between two numbers — by other words those two numbers the values of each vector I calculated in part 1. But this method returned the angles in radians which were a pain to work with.

Next step, I converted radians to degrees and tried to visualize the trigonometric circle in my head to start shooting these asteroids. Before reaching the solution I still had one other problem: the “laser” (0 degrees) was pointing to the right. To “hack” it, I added 90 degrees to every single angle and subtracted 360 degrees to every value bigger than that. Sorted the list of asteroids with angles and started iterating it. This should be it! Wrong solution. What did I do wrong this time?! After some debugging, found out that I was not filtering the closest asteroid in case there were multiple asteroids in the same angle (or line of sight). Fixed it and done.

This day was one, if not the hardest for me! Regardless it was very good to me, to recap some Mathematics background and how it can be applied in coding.

Day 11 — Space Police

Day 11 we needed to make a registration identifier using a painting robot. When I read this at first, I thought “Oh No”, but it revealed to be straightforward. At the beginning, all the panels in the grid where black and we could either paint a given tile black or white. And, of course to do this, we needed the help of our (now) beautiful IntCode program.

For part 1, the goal was to calculate how many panels were paint at least once. To do this, created a list of what I called GridMarks, an object containing an x coordinate, y coordinate and the tile color. Ran the given input on the IntCode program until it terminated. Each time it ran, it outputted the color that a tile should be painted and the direction to where the robot would turn. To help me with this, I used an enumeration instead of just numbers for the direction. Same with colors. It helps my reasoning when coding and helps people reading the code. Incremented my counter each time I checked there was a new tile being painted (by keeping track of it in the list of GridMarks and when the IntCode program terminated I returned the counter. Done!

Part 2 was similar to part 1, the main difference was the robot would start with painting a white tile as input. The goal was to print what the painting robot produces as a registration identifier. Made the same logic as in part 1 but then got a little bit stuck. “(…) a valid registration identifier is always eight capital letters”. I’ve seen something like this before… Day 8! But I had a list with points here. Started converting the list of GridMarks to a matrix of strings and applying a similar logic — “.” for black tile and “#” for white tile. Cool! Console.WriteLine it and… it seemed letters, but it was weird. They were mirrored! Got a mirror from my house and submitted the solution!! Then I fixed the bug and printed it correctly. Next level engineering.

Day 12 — The N-Body Problem

For Day 12 we needed to simulate how universe and gravity behaves. Cool! Four moons of Jupiter.

Part 1 we needed to calculate the total energy in the system of moons, given initial positions in 3D (x, y, z). To organize myself, implemented two classes: Coordinates (the name is self-explanatory) and Moon, with two Coordinates type properties — position and velocity. The stage was set. Applied gravity and velocity to each one of the dimensions in every possible moon combination (according to the description) and after 1000 steps, calculating the kinetic and potential energies alongside it, in order to calculate the total energy in the system. I was proud on how I organized my code! And it worked at the first run! Amazing! Even more proud! I wish all my code was like this 😊…

For part 2, the goal was to know when did the same positions and velocities repeated in the system. So, I started! “Of course, the universe might last for a very long time before repeating”. It was not going to get me any result… “Clearly, you might need to find a more efficient way to simulate the universe”. Yes, clearly! After some time thinking, Mathematics have taken the stage again! If I find the time when each dimension repeats, which is simpler and quicker, I can apply the Least Common Multiple for the three resulting numbers! After some minor mistakes implementing the logic and some errors, the logic was correct!

Day 13 — Care Package

Day 13 reminded me of arcade games when I was a little kid. Spent so many hours on these simple games!

Part 1 was simple for me. At this stage I was very confident in how my IntCode program worked. Applying OOP (Object-Oriented Programming), I had one class to register the dimensions and tile type and an enumeration for each tile type. Applied the logic in the description and made use of the Count method to know how many tiles where of block type. Nice!

Part 2 was a bit more complicated but fun. It was like playing a game! We needed to register the positions and the tile types as part 1 but this time, know the score given from the IntCode program when there were no more block tiles in the list. My main problem in this part was that I was not interpreting well the description and I had to read it several more times to fully understand the workflow. Interpretation is key!

Day 14 — Space Stoichiometry

I only began day 14 late night (weekend stuff).

And oh boy, it gave me a good headache… Both parts. It seems that at weekends the puzzles are more difficult! Or that or it is when I am less focused to do this…

For part 1, my first approach was to start at the Ore and go to the Fuel. After many unsuccessful tries and huge frustration, I gave up. I was just going to sleep, when I had the most obvious idea. Start from the fuel… Of course! How dumb can I be?? Implemented the solution but had an annoying bug because of rounding numbers. Even did three of the examples by hand to make sure what was happening. Fixed the damn bug and done. Time to bed!

Next day, for part 2, I had no idea again what to do. Let me go to Reddit… Binary search and guessing the value. Haven’t thought about that, was trying to go some “clever” solution to do that. Implemented the search to get the value I needed for the fuel and finished! Time to go for day 15 and try to finish it “today”!

Day 15 — Oxygen System

Oh the pain… Day 15.

Tried a bunch of stuff. DFS, BFS, explore the map first. Nothing. I tried to stick with the BFS which is seemed the most obvious to me. Had to post on Reddit two times. At first, the mistake was obvious honestly, but I did not see it. AAARGHHH! I was so frustrated with my code. Everything seemed to be correct after the fix!! Does the IntCode program have a bug or something?! Is it cloning the input to run the next IntCode program? Yes, yes, yes! Everything matched well. But… No success code from the output of the IntCode. I just don’t know what to do more. Time for a break, it’s Sunday, I need to relax.

Came back a little bit later for it! “Let me just try a DFS on my own, tracking everything”. And my problem was… To keep track of the commands (to then trackback in case a dead end was reached) I was using a Queue (which is LIFO) instead of a Stack (which is FIFO). I wanted a FIFO list and my mind did not thought that the problem could be in the Queue. Change the Queue to a Stack and worked immediately. Oooff, time to go to sleep.

Next day, after workday, started tackling part 2. Since I already had a Graph with the maze, I just had to calculate how many levels the graph had using BFS. Part 2 done quickly. If it wasn’t for that LIFO/FIFO structure confusion I would have finished these much earlier… ARRRGHHHH!

Day 16 — Flawed Frequency Transmission

After done day 15 part 2, I began Day 16 immediately. And this was the first day I thought about quitting doing Advent Of Code. “If this is going to take the same amount of time and problems as day 15, I will quit and do this when I have time.”

Started part 1 and it was just applying patterns to an input given. Parsing and doing part 1 was easy. Again, came the feeling that at weekends, puzzles are more difficult…

For part 2 was more difficult. I tried to apply the same algorithm as part 1 and it would take some millennia to run with such input. Tried one first approach after seeing that if the pattern grows, we would have more and more zeros applying to our input. I got rid of those dumb calculations and tried to run the input. Again, probably it would take some centuries to run. Then I started noticing that the last number is always the same, regardless of the pattern. Nice, seems we have some space to explore. I had the idea that at last phase, the pattern would be full of 0s unless the last digit. But didn’t knew what to do with that information. I reached Reddit and found what I was thinking but on steroids already. My reasoning was right, just needed a little push. The second to last digit would have a pattern full of 0s minus the last two digits (would be 1s) and then successively. Part 2 done!

Day 17 — Set and Forget

Day 17 and another puzzle which needs IntCode Program. For this one we need to track a robot. In some way like the last ones where the IntCode Program was needed.

Part 1 done quick. Just stored the positions given by the IntCode Program in a list and then just needed to see which ones contained scaffold intersections by checking if they are surrounded by scaffold. Then just multiplied the coordinates and summed all up. Done!

Part 2 could have been those faster too if I was not dumb. Why? Because I printed the path that robot was supposed to follow, written down the commands by hand according to the description but couldn’t find the match to have to reusable functions to give to the IntCode Program. Because I got two commands wrong. AHH the frustration… Even reached Reddit for this again. Regardless, not even close to the frustration I had previous days. After fixed the commands by hand, just gave them to the IntCode Program and printed the result. Done!

Day 18 — Many-Worlds Interpretation

Had a day rest from Advent Of Code. Yes, I needed it, had much work this day and needed to rest. So, Day 18 and Day 19 were done on the same day.

This was one was tough. Really tough. The goal was to get all keys in a maze. Each key had its corresponding door that would only open when such key was collected. Just at reading the description I smelled the toughness of this day. In part 1, started by collecting all the keys and doors in the input and making BFS to know shortest distance between all the keys. After that made a custom Dijkstra algorithm with states where each state was considered the same if it had collected the same keys. It took 15 minutes to run the puzzle. Done. Maybe later I’ll optimize it, or at least try.

Part 2 was a variation of Part 1, but with four starting points at the initial maze separated in four. Made some changes to my algorithm. And it took so long… I made all Day 19 part 1 and part 2 while this was still running… Must refactor and optimize all this… Someday, I hope!

Day 19 — Tractor Beam

After such struggle on the last ones, it is so good to find a puzzle that seems easy and straightforward… And it really was Day 19!!

Part 1 needed our IntCode program, which I feel very confident about. We needed to know how much positions in a grid would be in the way of a beam and count them, based on input that we would give to the IntCode program. Done in under 10 minutes with two loops. Awesome!

Part 2 was a little tricky but when I realized how it worked, it was quick and even made some optimizations. We needed to know at which position would a 100 by 100 spaceship fit on the beam. After some dumb mistakes I made, realized that the beam would go in a diagonal way, so I didn’t have to start from the beginning in every single row. Nice optimization! Part 2 done! I think this day was one of the fastest (apart from Day 1 probably). Disclaimer: Day 18, part 2 is still running…

Day 20 — Donut Maze

Another rest day. Started this on the weekend (corresponding to day 21).

Day 20 was very similar to day 18 — a maze and to solve the shortest path between two points but with different conditions, this time with portals that would teleport our robot to another position in the maze.

For part 1 I reused some code and adapted it to this part. For this part, I made a small method to edit the adjacency list of a graph, in order to connect portal positions. Then just reuse the BFS to define the shortest path between two points. Done!

Part 2, I don’t know why honestly, was having bugs with the part 1 approach, so I decided to do a custom BFS/Dijkstra algorithm, having into account the nested levels of the maze. And the solution took long to run… Not so much as day 18 part 2, “just” 8 minutes. Done but I will need to refactor some code and to try to optimize it.

Day 21 — Springdroid Adventure

Day 21 was mostly “pen and paper” puzzle, for both parts.

It remembered me of assembly code and registers I had in college. And in college I hated it honestly. Not much to say for this day apart that it having as a requirement the usage of the IntCode Program. It was more a matter of trying than anything else. Plus, later I have seen in Reddit that the solution was literally the same for everyone, if the IntCode Program was in a good shape.

Day 22 — Slam Shuffle

Day 22 comes.

Part 1 doesn’t even compare in difficulty to part 2. The goal of this day, is given a deck with a range of numbers and after some operations, getting the position of a certain number. Part 1 just implement such operations, easy, no difficulty.

Part 2 was “part 1 on steroids”. Huge numbers to make the same operations. Obviously, the same approach would take some millennia to do. I thought the most obvious thing would be that the input would repeat itself given some time… Never happened (at least while I ran it with part 1 approach. After much thinking… I had no idea how to solve this. Had to go to Reddit for some directions. And concluded that this part 2 was more a math problem than anything else. It needed the use of modular inverse and fast exponentiation: things I had forgot from college. I would like to thank to the Reddit user mcpower_ (/u/mcpower_) for the great explanation about how to solve the exercise. Mapped the explanation to code and done. Did I cheat? Yes. I had no idea how to do this puzzle.

Day 23 — Category Six

We are almost at the end of Advent of Code. It is day 23 and IntCode Programs are here again! Cool! After day 22, this puzzle was much easier, or I would call it solvable eheh.

Part 1 needed 50 IntCode computers to run and to talk to each other like a network. When we noticed a specific address, we needed to return the Y value. Done with no problem nor issues.

Part 2 was a small improvement from part 1: save the last packet sent to a specific address and return which is the first Y value to be repeated when sent to address 0. Done! Very easy day, done in half an hour!

Day 24 — Planet of Discord

It is day 24 and Christmas Eve.

The main theme of this day was bugs. Not in a programmatic point of view thankfully. We needed to find bugs in a grid and calculate how they would appear and disappear over time, based on the provided specification.

Part 1 was straightforward and the main difficult in my point of view was cloning the objects so references could not be messed up with the manipulation we needed to do over time. Done in the morning!

Part 2 took me a little more time and only finished it after dinner. Recursive spaces with the same rules as part 1. The major difficulty was to find a way to fetch the needed adjacent positions. And I must be honest: I’m very proud with my code structure for this day. I think the modular and “readable” way of it, made me got the solution faster and with lesser bugs. Part 2 done with a nice Christmas dinner in my belly.

Day 25 — Cryostasis

Day 25. Merry Christmas! Last day! And what better way to finish than with an IntCode based puzzle? We reached Santa’s ship, but something is missing, and we need to go through a small maze to get it.

For part 1 I just took the approach to do it manually. Made an infinite while loop that reads commands from with Console.ReadLine and ran the program via command-line with my unit test. Gathered the items quickly but it took me some time to find the right combination of items to get the password on the pressure-sensitive floor. I could make this automatic now that I know the answer but I’m not going to make it. Done.

For part 2 it is saying I need all 49 stars prior. Nice! I’ve made them all, let’s go get the 50th! And… Done! The warp drive was aligned and the 50th star got! A very fun day! I feel so proud of myself that I completed this within the 25 days of it!

Conclusion

I loved doing these puzzles. I really must thank Eric Wastl for making them. I’m sure it is more difficult doing them than solving them!

I will never get confused about which FIFO and LIFO correspond the Stack and the Queue data structures in C#! Path-finding algorithms (BFS, DFS, Dijkstra) were sometimes difficult to implement but they are so good for maze solving. Modular and “readable” code make coding easier. Much easier! And IntCode! What a great idea!

I had the personal goal of trying to make all puzzles within the 25 days of it and I made it! Some puzzles took more than a day to do for various reasons but at the end, the main goal was reached. I feel so proud!

I am no genius; I am just a hard worker. If I can do it, everyone can do it.

Link for the same article in my blog here.

Github