11
votes
Day 6: Universal Orbit Map
Today's problem description: https://adventofcode.com/2019/day/6
Join the Tildes private leaderboard! You can do that on this page, by entering join code 730956-de85ce0c
.
Please post your solutions in your own top-level comment. Here's a template you can copy-paste into your comment to format it nicely, with the code collapsed by default inside an expandable section with syntax highlighting (you can replace python
with any of the "short names" listed in this page of supported languages):
<details>
<summary>Part 1</summary>
```python
Your code here.
```
</details>
Well, today's challenge seemed pretty easy. Unfortunately, I got stuck for a bit because I put my parsing on the iteration block instead of
END
and made a typo on my answer. Here are my very hacky solutions which I will clean up later:Part 1
Part 2
EDIT: Cleaned up a bit, but part 2 is probably still pretty hacky. Too bad AWK can't break through multiple loops like Bash.
EDIT 2: Settled for a less fancy but more readable solution.
Been doing it in rust this year, all solutions so far on my github.
I feel like my code is really quite messy and probably could have benefited from a proper graph library, rather than a hashmap of
node => value
and having the edges asvec[(start, end)]
.Part 1
Part 2
I just realized that part of the reason I feel that my code is dirty is because the implicit priority is to write code quickly, not to write good code.
With this in mind I put my current very dirty code and hope that I will have the time to clean it up in a future edit.
Solutions in C++:
Part 1
Constructs an adjacency list representing the directed graph of orbits by turning every string into a number. The problem is then equivalent to counting the number of paths on a Directed Acyclic Graph, which is done here using dynamic programming (though the number of paths is small enough that it isn't needed)Part 2
Changes to Part 1: Make the graph undirected (construct edges both ways) and perform a breadth first search from the node representing YOU to the node representing SAN, and return its distance (minus 2, since YOU and SAN don't themselves count).Well, it depends how you look at it! I didn't pay any mind to the leaderboard for most of the challenges, especially since I started on day 3. I just tried to get a decent place for once today.
EDIT: Also, it isn't really fair, anyway, since it opens at the same time which would be midnight and such in other timezones, so I wouldn't really sweat it.
That's a fair way to put it.
It is quite fun to have the "act now, worry later" mentality when coding for speed, but it's definitely easier to be calm about the problem when you're a couple of hours out from any leaderboard thoughts.
I just caught up, so this is the first day I actually got points on the leaderboard for, but it was still pretty fun. I didn't break up my code by part 1 and 2 though.
Day 6 - Universal Orbital Map - Python
For this I use recursion and then more recursion input_string_list is just a massive string literal that contains the line-separated list Basically I build a dict with each planet and whatever is orbiting it. Then I started at 'COM' and recursively looked at it's children and calculated their distance from 'COM'Then for Part 2 I built a function that recursively finds the path between a given planet and 'COM' and then provides that as a list. I took that list for 'YOU' and 'SAN' and looked for which parts of the path were shared.
The last element on the shared list is node they both have to go through, so some quick distance calculations (distance 'YOU' vs distance 'SAN' vs distance SHARED) and a minor correction for distance of you vs distance of what you're orbiting and I had the answer
This time the problem was fairly easy to understand, but I struggled surprisingly much this this one. I probably chose a bad data structure, and unfamiliarity with functional approach to this kind of problem didn't exactly help.
Especially the solution to first part feels really unclean: I basically create a very complex recursive list, and then just... flatten and sum it.
I actually like my solution to the second part though, it feels kind of elegant.
Part 1 and few common functions
Part 2
This was a fun one! Something feels funny to me about some of the methods I made for traversing the tree but they work so I'm not too miffed. Also, I thought at first that the method of finding the COM had to be general for any string as the COM which it didn't, luckily that wasn't too hard. As I'm going along, I'm using more list comprehensions, which aren't a Python feature I have generally thought to use that often, and they are growing on me. I guess they are just syntactic sugar in a way, but much easier than loops for some tasks.
Parts 1 and 2, Python
I didn't read through much of your code, but spotted something on a quick skim that has the potential to cause issues that I wanted to mention. I'll hide it inside an expandable block since other people might not care about Python trivia:
The dangerous part is using a list as a default argument value, which can have surprising behavior in Python, because lists are mutable and the function definition is only processed a single time when the code is interpreted, not when it's called. That explanation probably doesn't help to clarify, so let me try to demonstrate the unexpected behavior:
That is, both objects ended up sharing the same list as their
items
property, instead of each getting their own. The same thing happens with functions:The obvious expectation would be that if you call that function without an argument, it would always print
[1]
, but it doesn't. The mutable default argument gets modified each time the function is called and persists.The usual way to avoid this issue is by writing something more like this:
That way the blank list is being initiated inside the actual function code instead of the definition, so a new list is created every time it's called.
Ah that makes so much sense! It actually gave me some trouble which is why there are a few places where I would have wanted to allow the default argument but specified it instead because of that issue. If you have a chance, would you mind explaining how the
items = items or []
statement works? It looks to me like it should return assign aTrue
orFalse
toitems
but it doesn't.Yeah, that's another minor strange thing with Python: using
or
orand
can return non-boolean values when it was used on those values. They return the last value they checked before stopping, so inor
's case, it will return the first non-False-like value:And if they're all False-like, it still returns the last item. When you're using it in a context looking for a boolean result, that will still be treated as False, so it works fine.
and
is kind of the opposite: it will return either the first False-like value, or the final value.So then the
items = items or []
basically means "ifitems
isn't something that evaluates to boolean false, use it. But if it is false, assign an empty list instead."To be more explicit, you could also just write it as:
Yeah that is a pretty spooky way for
or
to operate. Thanks!I enjoyed this one, unlike some others, many people are using graphs when they are not needed at all.
A dictionary (map) and two sets is all that's needed to solve this one.
Part 1 and 2, OCaml
The Utils function used can be seen in https://gitlab.com/unduthegun/adventofcode2019/blob/master/00/utils.ml
This puzzle was good practice with pointers, which I'm a little rusty on. I've started using Go at work, and it's the first language with explicit pointers that I've used since C and C++ in some college courses.
My approach was to create a standard tree data structure for the orbits, but also simultaneously create a map that associates each celestial object name with a pointer to its node in the tree. This made for easy and quick access to any node in the tree, from which it could be traversed as needed.
Loading the input from a file into a string isn't included in this code because I keep that logic in a separate package that's used by all of my solutions.
Parts 1 and 2, Go
My first language was Java, and there was a lot of talk about "design patterns" and UML and so on that lead me to pick up habits I've never quite unlearned, leaving me with a tendency to be over-abstracted and object-oriented. My solution here suffers a bit compared to some of the others.
When dealing with a graph, I usually tend to think in terms of an actual object graph with node objects and references on the heap, rather than just using a map of
node => (linked nodes)
as in some of the simpler and more elegant solutions.That said, it does pay off sometimes; I'm pretty happy with where my Intcode interpreter is at, and I'm excited for more of those puzzles.
I don't expect to re-use much of this, I'll probably re-do it all on the next graph problem, but it does work and it was quick to write.
Day 6, Crystal
I've also put all my solutions up on my github
I got a later start than I wanted to (by several hours), but this one was more fun and easier to understand than the previous day by a good amount.
Part 1
Part 2