Advent of Code 2022

Advent of Code is back! I'll be updating this post with my thoughts on each day throughout the challenge. The full source for my solutions can be found in this git repo. I was looking into using zig or c this year, but I am going to go with python. I think this will be the fastest way for me to complete each challenge, which is a priority for me this year.

Day 01

Today was a typical AoC Day 1. Part 1 was a simple string split on "\n\n" and then a max of sums. Part 2 was sorting these sums and returning the sum of the 3 largest. I'm happy to be kicking things off again this year. As-is, I am pretty happy with my template.

Day 02

These problems were straightforward, but my solution was a bit of a mess. The key insight (I think) is to map the input chars to their numbers and then perform arithmetic with modulus 3 on the result. I think this would give the answers in the cleanest way possible, but I know that I always struggle with 1-indexed modulus, so I wrote out some extra if statements that I was sure are correct. I managed to get ranks 1270 and 858 respectively, which I am happy about. Maybe I'll polish this solution up in the morning.

Day 03

This was one of those days where I did not see an obvious easy answer until I was working on the second part: use set intersections. Once I realized this, I refactored my part 1 and it seems to read a bit better. Also, I'm sure there is an easier way to map from chars to "type", but I used a dict in my implementation. I enjoyed these 2 problems.

Day 04

Another day where sets come to the rescue! I was initially going to use a simple dataclass to track each range, but I realized that sets may be a better choice. The first part was checking for supersets and the second part was checking if the intersection was non-null. I complicated things for myself by insisting on using a list comprehension to parse the input, which is something I don't like doing: but I got the problem solved fairly quickly. Today was fun!

Day 05

This was a good problem, but I did not sort out the parsing as fast as I would have liked. I went down the path of trying to split the input strings from the start, when I should have thought for a moment first: using direct string indices works better. I figured we wouldn't have a scaling issue in part 2 as this is only day 5, so I went ahead and implemented the stacks as described in the problem. For part 2, I pushed to a temporary stack during the swap and then pushed to the destination pile in reverse. I could have done this in a different way, but I was confident I would get this approach correct faster.

Day 06

At first, I tried to do this a responsible way and track the value of each character, returning early when the marker was found. After a few minutes of issues with this, I went with the obvious approach for me. This was to iterate over the whole input string and check if every window of 4 characters was unique. I did this by constructing a set and checking if the length was still 4. While this is likely not the best performance-wise, thanks to the string slice and set construction, it was the easiest to write. And today's part 2 was a simple extension of the problem, growing the window from 4 to 14. Instead of challenging runtime, this tweak challenges how easily adaptable the part 1 code was, which I think is interesting. For me, I copy/pasted the part 1 answer and change the 4's to 14's: which was all I needed to do. I enjoyed today's problems!

Day 07

Today was a bit of a struggle. The problem itself was not too complex, I simply could not come up with a good data structure to represent the problem. Days like these are the most challenging for me. I ended restarting 30 minutes in and cramming everything into nested dictionaries. I then used a recursive walk to calculate the directory sizes. The end instructions for the final sum were a bit confusing, but I was eventually able to work them out. Thankfully, part 2 only modified the final calculation and did not introduce a larger scale change. I need to brush up on my tree representations and read about more elegant ways to parse this input and represent it in memory.

Day 08

I thought today's problem was right near the sweet spot of difficulty for me, given this is a weekday puzzle. The instructions were a little opaque, but not impossible to understand. The task itself had an obvious solution for both parts, but brought in concerns about runtime. I took the naive approach for both parts, performing the scans of the grid as laid out in the question. I'm confident that there is a much faster solution, and I think dynamic programming may be possible. I have no plans to look for such optimizations this year, and I will certainly not be looking for them in my first solution. I enjoyed today!

Day 09

Today was fun! I expected that the second part would either be complicating the rules, but the longer rope was an interesting surprise. I was able to refactor part 1 to be general in just a few minutes, and get the part 2 answer without much trouble. In part 1, I was tripped up and tried updating the head the full amount of steps all at once. Once I realized my mistake, and wrote out the core update code in a verbose way, I didn't have any issues. This was a good problem!

Day 10

This was a hard day. One of the core challenges of AoC is reading and understanding the questions. The best solvers in the world can do this in seconds. I struggled to understand today's question for ~40 minutes. I'm not quite sure if I can articulate what I thought the question was asking. I eventually deleted my code and re-read the prompt, and was able to hack something together for both parts. I have no idea what a good answer would look like, but I can guarantee that my solution is not it. I need to caffeine.

Day 11

Today was not fun. There was a lot of careful reading required. I wrote a bunch of boilerplate to represent the problem clearly. I then go hung up on some weird python issues; lambdas seemed to be changing the value they would return. There was a lot of bad code written today. Then, I could not figure out part 2. I knew modulo would be involved, but I could not see that we needed the LCM of the divisors. If I had noticed that they were prime numbers sooner I may have realized that I needed to multiply them. I need to brush up on my modular arithmetic.

Day 12

I am struggling with reading comprehension after just waking up. Recognizing the solution and coding it up was trivial, I wrote both BFS and Dijkstra's. I could not figure out what was wrong. The first gotcha was that elves can jump down any height without issue, so only neighbors more than one higher the current position had to be excluded. The second gotcha was the values to assign to S and E. I mistakenly chose -1 and 26, when the problem clearly states they should be the same as a and z (0 and 25). I explicitly looked for this information at the start, and missed it. I wasted most of the time today looking for this information again. The example input does not have a y adjacent to E, so this case is not tested; the z must be visited first anyway.

I desperately need to find a better time to work on these problems, because I am creating all kinds of unnecessary complications when trying to do them half-asleep.

Day 13

Today was fun! I used python's eval to parse the list strings into python lists directly. I was stuck for a while returning bools, when I realized that I needed to write a true cmp style function, returning -1, 0, or 1 instead. This lead perfectly into part 2, where we had to use such a comparison function to sort all of the packets. I was concerned for a moment, but then I found functools.cmp_to_key, which allows you to pass a cmp style function to the key argument of sorted. This made everything straightforward and I got my part 2 answer without issue. Taking a few more minutes to wake up before starting this morning was a good decision.

Day 14

This was a good problem. I again took some extra time to wake up this morning and read the problem slowly. I implemented everything as the problem laid out, and expected either a scaling or a rule complication in part 2. We were lucky that today did not require major changes to the naive approach in part 2. I enjoyed this problem, and I am looking forward to seeing what other solutions looked like. I have a hunch that there is much more efficient way to simulate the sand (without simulating it directly), but have not put much thought into it.

Day 15

Today was fun. I figured we would have scaling issues, given the size of the input numbers. Part 1 was fairly straightforward, the only hangup being what bounds to use when checking the row. Part 2 was hard, but it was fun. I realized that the solution must be 1 step off of the border of one of the coverage regions. So I took the most naive approach which was to add all of these eligible values just off the borders and check each one. The solution is slow, but it takes ~1 minute on my machine, which I am happy with. Outside of SAT solvers and other hyper aggressive solutions, I'm sure there is much performance to be gained with my approach as it wastes a bunch of work. Instead of using an intermediary set, I could check points as I go for example. I enjoyed this problem!

Day 16

I did not finish Advent of Code this year. Part 2 of this problem stopped me in my tracks. I was struggling to find the time to work on AoC, especially with the holidays. I may pick back up with these problems at some point, and I'll update here if I do.