Advent of Code
While I've never actually finsished the calendar, I just wanted to share my take on the Advent of Code project.
Advent of Code is pretty much what it sounds like. It is an advent calendar featuring code puzzles with some cute story, and gamification aspects. For the 25 days of advent, every day has a story, a puzzle, and then an optional extra challenge to let you earn that extra star every day.
My experience with it
I learned about it some 5 years back, and did the first 10 days in Python. The year after, I did 8 days, again with Python.
December of '22, I wanted to give it another whirl, but this time with C#, and I actually had some very interesting outcomes. I haven't gotten very far, because the work schedule picked up quickly, and I didn't find much energy for coding outside of work, but for the days I did do, I had a really good time.
If you want to keep tabs on my progress, my repo is publically available on Github.
Before getting into my learnings, let me share my approach.
My approach
Let me outline the approach briefly, and then go into detail:
- Every day comes with a story, a challenge, a small input sample along with expected results
- Then, a long - personal - text file with inputs (personal, so you cannot share results. Every player will have different results)
- After solving the challenge, you get a followup extra challenge, that'll usually build on the first challenge
When I worked on the challenges back when using Python; I found myself reaching for the quickest solution. I started coding line by line, and once stuff got messy, I'd clean things up, moving lines into functions, so I wouldn't repeat myself too much.
It was fine, but in many cases, once the followup challenge was revealed, I went "F*ยค#!", because my messy code couldn't really be adjusted to account for the changed requirements. After 10 days, I left it, because it became too hard to deal with my mess.
The year after, I gave it another go, and this time I thought I'd take the example input and output and turn that into unit-tests, so I could verify my code along the way. This helped, but I still faced the same problem, that my code was too hard to change. Often, I'd change it so it would solve the followup challenge, but then it would no longer solve the original challenge, leading to failing tests. This lead me to copy-paste, so I had some code for the first challenge, and a modified copy for the followup challenge. This was demotivating, and I ended up leaving it.
Years later, now in '22, I picked up the mantle again. This time with C#. Now that I knew how the problems would be structured, and how the followup challenge would build on the initial challenge, I figured I'd try to attack it with proper abstractions, and do my best to adhere to SOLID principles along the way (without going overboard with unneeded design).
This was a GAME CHANGER..!!
I am 6 days in only, but for every one of these days; every time I got the followup challenge, I found that I was well prepared for it! If you look at the commits in the repo linked above and focus on the ones for "part2", you'll see that each one is only a minor adjustment to the design of "part1". I am particularly proud of the CrateMover9001 challenge (Full description here).
I had taken the approach that the state of the crates was an object, and it knew how to modify itself. I wasn't completely happy with it, but I knew that - if necessary - I could break it up with the Strategy pattern if needed.
Once part 2 was revealed, I saw that it did exactly call for the strategy pattern! I created the ICrane
interface, and
implemented the two cranes as objects that could act on the crate stack state, and it just clicked!
public class CrateMover9000 : ICrane
{
public void Move(StackCollection stacks, List<MoveInstruction> instructions)
{
foreach (var instruction in instructions)
{
Enumerable.Repeat(0, instruction.Count).ToList()
.ForEach(moveCount => stacks.Move(instruction.From, instruction.To));
}
}
}
public class CrateMover9001 : ICrane
{
public void Move(StackCollection stacks, List<MoveInstruction> instructions)
{
Stack<ShippingContainer> tempStack = new Stack<ShippingContainer>();
foreach (var instruction in instructions)
{
Enumerable.Repeat(0, instruction.Count).ToList()
.ForEach(moveCount =>
{
tempStack.Push(stacks.PopFromStack(instruction.From));
});
while (tempStack.Count > 0)
{
stacks.PushToStack(instruction.To, tempStack.Pop());
}
}
}
}
Lesson learned
While - of course - these a small problems and not huge enterprise applications, my experience here really illustrates to me that thinking in terms of patterns and thinking about SOLID principles, will really lead to code that is easier to change, manage, adjust, and re-use. Everything I couldn't do with Python back then, I could easily get with C#!
Is this Python's fault? Of course not. I've grown since then and learned a lot, and surely, I could have done the same with Python. However, I definitely prefer C# at this point, because it lends itself so well towards the patterns and principles that are now so deeply ingrained in the industry. Python is easy to write, but also easily becomes hard to maintain. While with C# or other statically typed languages, it takes a bit more effort to write, but it also forces you to think of classes and how they relate, because if you don't, you'll end up with something that doesn't even compile.
Closing thoughts
All in all, aside from sharing my fun experience, I just wanted to shine a light on the Advent of Code project. I would encourage any coder to give it a try. It may not be for everyone, but definitely worth a view!