9
votes
Day 14: Docking Data
Today's problem description: https://adventofcode.com/2020/day/14
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>
Python
Repo Link
Part 1
For the first part, the thing that got me that it wasn't actually a bitmask (not a traditional one anyway). You just had to compare each element in the value and the mask pairwise to produce the "masked" value. I took advantage of Python's nifty
format
andint
functions to parse the data into and out of binary representation.Part 2
For the second part, the main challenge was how to generate all the new addresses from the "masked" address (ie. how to expand those floating 'X's). What I did was a generated all the permutations of '01' with a length equal to the number of floating numbers, and then I replaced the X's in the address by the elements of each permutation (the code says
product
). For each of these new addresses, I yield from my generator function and then used that address to store the current value.Part 2 (Alternative)
I made an alternative version of my
expand_addresses
function in the form of a recursive generator (and avoids usingitertools
). The idea here is that you scan across the address until you hit anX
. Once you do, you recurse twice: first replacing the 'X' at the current index with a0
and another by replacing the 'X' with a1
. If the current digit is not anX
, you just recurse to the next index. The base case is simply when the index has reached the length of the string (ie. theWORD_SIZE
or36
for this problem).Urgh. Gonna post my solutions tomorrow after sleeping. Today, the debugging fix was "Listen to the damn compiler warnings that you're doing a sign-extend where you probably don't want to". Spent 10 minutes trying other stuff while ignoring the warning VS was giving me.
Still on top of the private leaderboard, but only by a single point. Probably won't be able to keep it up much longer.
Rust
This is probably my sloppiest, hackiest solution of any AoC ever. I did everything in strings since I don't have a strong understanding of bitwise operations. This thing is riddled with hacks to fix things that went wrong for reasons I don't understand. The basic approach was to
apply_mask
first and then generate all the combinations of wildcardX
. The problem is that I generate a ton of incorrect memory addresses and discard them based on string lengths and whether or not there are still any X's in them.Totally open to any suggestions to clean up this monstrosity, especially the
floating_addresses
function (while staying within the confines of this overall method method, ie, very little bitwise math, since I don't like to incorporate stuff I don't understand).Code
That one was fun! It felt like a good mix of stuff. My solution used some string replacement, more than a little type conversion, a touch of regex, and even some recursion!
Have some more eurobeat
Day 14 Part A – Python
For this first one I pre-allocated some memory and then turned my value into a list of characters so that I could play with the digits and override them as necessary. The mask just stores which bits get replaced in a key, value pair. Then when I was done with the processing, just turn into an int and dump it into memory
I also googled a decimal to binary method, because I didn't feel like writing one myself.
I also padded my value all the way up to 36 bits, so that I wouldn't to worry about indexing and what-not. It would always be 36 bits long. I had originally though about reversing the order of the list instead, but I though the zero-padding was a more elegant solution for the index problem. Less of a headache too.
Day 14 Part B – Python
This one was a bit more complicated. My type conversion worked well enough, but I had to do it on the address, instead of the value. And I copied over the entire mask, instead of just the 0's and 1's. Also I switched memory over to a dict, because 2^36 is a LOT of addresses to try to hold in my RAM.
I decided to go with a recursive solution to split my addresses. It would walk through a given address. If it ever ran into an 'X' it would make 2 copies of the address, one with a 1 and the other with a 0. And it would call itself on both of them. The tricky part was making sure that I had my data storage correct when passing between levels of the recursion. Which is to say, the raw address vs a list containing multiple addresses
I realized later that another method that could work would be to generate a list of all X -> 0/1 combinations when you process the mask. Then apply that over each address the same way you did in part 1. It's probably slightly more efficient too.
Tips and Commentary
I've mentioned regex a few times now, but I don't think I ever explained what it is. Regex is shorthand for 'regular expressions'. They're a way of searching for specific patterns within text. So for example, if you needed to find something formatted like a street address, you could use a regular expression to find that. They tend to look like a bunch of arcane symbols until you learn to read them, but they can be extremely useful. I highly recommend https://regexone.com/ for learning how to actually use regex. And https://regexr.com/ is great for testing them out. I probably should have mentioned that like a week ago
When working with the mask, it's going to be important to know what index you're working with. Especially since some of your numbers will be shorter than the relevant digits in your mask. There are a number of ways to deal with this such as converting to binary and then left-padding it.
I ended up using a lot of type conversion for this one. Some of the different component problems were much easier in different forms, so I swapped between them as was appropriate for the given task
You're going to need to generate a bunch of addresses. There are a few ways to do it, for example, I used recursion. But interestingly, a given mask should always generate the same number of addresses and configuration of bits that need to be overwritten.
The memory addresses get much bigger in Part B. Pre-allocating a fixed area works fine for part A, but don't try it for part B unless you're running doing AoC on a supercomputer.
Got a late start due to a Dota match, but this was pretty simple. My solution is stringier than I like and a bit inelegant, but it works well enough.
Part 1
Part 2
After I finished I saw a lot of other solutions splitting out the mask into two separate masks, one for the bits to zero, and one for the bits to set, which lets you directly apply the mask via standard bitwise operations. I like that a lot better than the stringy iterations I'm doing for each one, but it works well enough.Python
I had to put yesterday's part 2 on hold but plan on trying to solve it without looking it up (I suspect it's some modulo arithmetic and I'm having flashbacks to last year). Today's is more straightforward. I'm never feeling cozy with bitwise operations but I guess it was bearable.
Sidenote: I just love that Python has a plain sum() function. The amount of loops just summing up the values of lists I've written... it seems so obvious to just have this but it apparently isn't.
Part 1+2
This one was a little tough to grok at first, but turned out not as bad as yesterday for me. I think my solution is decent, especially since as far as I can tell, Julia is woefully underequipped for bitwise operations, so I did pretty much everything with strings. I guess that makes sense as a young language focused on scientific computing, but I wasn't happy about it today lol. It's still pretty quick though, both parts take about half a second, and I think the vast majority of that time is spent in the recursive function I defined for figuring out the memory locations in part 2. It seems like I have a knack for writing recursive functions that really hit the weak points of Julia's memory management.
Part 1
Part 2 diff
This took me a while because AWK has no bitwise operators and such (and
substr
/match
hell), but the problem itself was fun. I also learned that POSIX AWK has while loops and functions, which would've made some other problems easier... I really should stop putting off reading the full POSIX manual.Part 1
Part 2