/advent_of_code_2021

Primary LanguageClojureEclipse Public License 1.0EPL-1.0

advent_of_code_2021

Repo for advent of code 2021 in clojure. I assumed that each day would build on the previous but 3 days in and I’m realizing that wasn’t the case. So for now I’m putting code for each day in it’s own ns with very little shared code between.

Code is copied from previous days where useful instead of moving into a common place, since I don’t know when it might need to change in a way that would break the answer for an earlier day.

In some cases (it’s happened on day 2 already) the ask is to change the code in task 2 that is required to get the correct answer for task 1, so occasionally functions may get copied within a file as well.

For the most part there’s a comment block at the bottom of each day that has `tests` for the correct answers. I’m doing this instead of actual test namespaces for simplicity, it might be worthwhile to move them all to actual test files later… I’m writing this on day three, so who knows what will happen by the end.

Installation

Download from https://github.com/jgerman/advent_of_code_2021

Days

It occurred to me after the fact that I should have kept notes on each day, this is day 11, so I’ll start there and maybe go back and add the rest.

Day 8

This solution is a mess, it worked but it took far too long, is hard to read, fragile, and embarrassing. This is the result of trying to crank something out after a long day of work and when I wasn’t in the right mindset. Stepping away from the machine would have been smart… rather than just bashing through step by step.

Conceptually it’s simple, given certain knowns you can find everything else. Even though the execution time would take longer I feel like using core.logic (or something similar), or implementing enough of it to just state the facts and let the rules fire would be the cleanest way to solve the problem.

This is high on the list to come back and redo.

Day 11

This was pretty easy, I spent some time looking for a function in core.matrix to give me all the locations that matched a predicate. I couldn’t find it but I can’t imagine it doesn’t exist.

For both tasks I just recurred over the steps and returned a map with the state and whatever I needed to track, but I think this could be better handled by using a lazy sequence. So when I go back to write it that’s what I’ll likely do.

Day 12

I played with Loom a little bit before deciding to just code it up and boy did I make a mess when I first did it. So much so that task two caused me issues, depspite it just being a simple logic check.

Some lessons learned:

  • Sitting on the couch with a laptop watching football is not the ideal way to solve a puzzle.
  • Stop getting cute with the problems.

As soon as I went downstairs away from distractions, I re-wrote the traversal algorithm in a few minutes. I think there’s still a few left over things that I didn’t clean up, but in the end I wound up starting over for task 2 and refactoring task 1 to use that code.

I really made things hard to reason about by looking at the children and trying to decide if they were worth considering, instead of looking at the current node only and moving on.. .probably some sort of moral in that story…

Reminder for when I come back to it: The performance isn’t great, I think that’s probably down to the mapcat in the recursion. I couldn’t get it all in a single list and I didn’t want to fight with it, so that’s a spot to revisit.

Day 13

Super easy, just map over the coords doing the transforms then plot out the points.

Day 14

Task 1 was easy, got the wrong answer before I realized I was still running it on the sample rather than my actual input.

For Task 2 just following the rules won’t do the trick, it’ll take far too long and probably blow out available memory.

I realized how stupid I was being early today at work, made the mistake I didn’t make with the lantern fish. Wound up re-writing the whole thing, it’s fairly gnarly but it works and returns the task 2 answer immediately.

Another candidate to refactor for clarity.

Day 15

I went ahead and used Loom for this, on the one hand now I know loom a little better, on the other I didn’t implement Dijkstra’s myself. This is a place to come back and re-implement by hand.

I got a little gross with task 2, I’m sure there’s an elegant way to handle the increased size without literally making a map of the increased size. But it was easy so I just bashed my way through it.

Day 16

This is just an analogue to parsing a variable field length binary file format. Pretty straightforward to write.

Got cute with the eval logic.. and it burned me. I do need to come back and eval the tree for the versions, I just saw the opportunity to calculate version during the parse and get it quickly and went for it.

Day 17

Brute force today, starting to sense a theme as the event goes on, I get a little sloppier, it’s fine because my goal is to complete both starts on that day throughout. I would like to come back and clean up my solutions, but I’ve definitely started to lean more towards getting the answers and moving on.

My first goal: changing my clj workflow to use deps.edn, Reveal, do less direct work in the repl in favor of evaluating from source files has been achieved. I somehow missed out on `(tap>)` which has been a huge help. Between that, the step debugger, and Reveal, I’ve been pretty happy. I’m not sure I need Reveal vs just dumping data to the repl via tap> but I’ll bet the day I need a feature it has, like visualization, I’ll wish I had it. On the other hand I prefer to leave Emacs as little as possilbe and the Reveal window takes screen real estate.

Day 18

What. A. Mess.

I looked at this Saturday morning, realized it was just tree operations… I dabbled a bit with parsing the expressions then went right to zippers. By the time I got back to it late Saturday night I realized that I didn’t know zippers as well as I thought.

Here I am Sunday trying to catch up… that was a struggle and points to a place where I need more practice. I ground it out but this day’s source is a mess, maybe even more so than day 8.

First task I DNF on the day it was released. Very likely Day 19 will be the same but we’ll see.

The messy source gets worse as I go, plenty to go back and refactor and improve on.

Day 19

Giving myself a break on 19, if I get to it before the 25th so be it, otherwise it’s not going anywhere

Day 20

After taking Day 19 off this was pretty easy. I initially put the oob calculation in, thought I had overengineered, considered just leaving it in but took it out for the sake of cleanliness…. then I had to put it back, but in a slightly different form.

Not sure how I psyched myself in and out of that but I did :)

Technically the oob should be based on both the first and last values of the algorithm, but I didn’t bother fixing that since in the solutions so far they’re always complementary.

Day 21

Task 1 was trivial. Task 2 took me longer to wrap my head around, I made the mistake of looking for hints in the subreddit because I assumed some trick was necessasary. That wound up spinning me out a bit. I should have just built the tree, memoization makes it fast but it still returns in a reasonable amount of time without.

Once I stopped trying to pre-optimize it was easy to write.

Day 22

Brute forced task 1, no problem.

Tried a couple of wild things for task 2 then based on some hints worked out how to loop through adding on cubes, plus any compensation cubes for overlap, then how off cubes interacted.

My sample 2 answer was off by 10 for some reason, on a whim I decided to try against the full input and see what popped out, and it turned out to be the right answer… Something to sort out at some point I guess.

This needs cleanup, and task 1 should be re-written in terms of task-2.

Day 23

First commit of this has no code, I solved it with colored cubes and graph paper. Which was silly in a way since I knew I’d want a solver for Part 2.