13
votes
Day 4: Passport Processing
Today's problem description: https://adventofcode.com/2020/day/4
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>
Aaaah that's so nice with the Lambda Function Pointers. I was writing my fourth if-elif-branch and thought "Well, this is annoying?!" and I totally forgot about that trick.
And using regex looks also way nicer than my "let's just convert the string to an int and see if that works" approach.
Wow Part B was annoying! This one has a lot of small bits that need to be exactly right and it's hard to track down exactly what's wrong. Make sure to use the examples!
Day 4 Part A - Python
The tricky part here is that you need to track stuff across multiple lines. There are a number of ways to do this, I chose to build a password database, but you could easily just check each passport as it goes through. I wasn't sure what part B would be like, so I possibly overdesigned it.I was tempted to use regex in part A to do shove each passport into a dictionary, but I realized splitting on spaces and then again on ':' would accomplish what I wanted more easily. I ended up having to use some regex in part B anyway.
Day 4 Part B - Python
FUCKING HELL THIS WAS ANNOYING. So many fiddly bits. Basically I started by assuming any given passport is valid and checking if there was any reason to invalidate it. I designed an simple check for each criteria and then ran every passport through them. If a given passport passes all the checks, it's valid. I used a mixture of standard string functions, some type casting, and some regex for good measure. Basically whatever seemed best for each individual question.I'm not really happy with my if-else chain, but it did get the job done
Tips for Beginners
This problem in particular makes it very easy to make a mistake and not understand why your answer isn't coming out correct
When you're looking at a given passport, you need to be able to manage taking in your data from several different lines. There are several different ways to do this. Personally, I loaded my data into a dictionary (a specific Python data structure). Whenever I saw a blank line, I knew I was done gathering data for the from that passport, so I moved on and created a new dictionary for the next passport. You could either check each passport as it comes in, or you could wait until you have them all stored. There are pros and cons to both approaches, but that discussion is beyond the scope of this problem
Part B is MUCH harder than Part A. At least it was for me. Don't get discouraged if it gives you trouble. Just breath. You might need to spend some time deep diving
You have several different characteristics you need to check. If even 1 is wrong, the whole thing comes back wrong. So you need to structure your code in a way that a single error will invalidate the whole thing. I started by assuming every passport was valid. Then I checked it against each of the criteria I was looking for, If any of them was wrong, I flipped a flag that said the whole passport was invalid. Then at the end, I check if the flag was still valid. If nothing had flipped it, that means it made it through all the checks. It's sort of like checking it in reverse.
There are other ways you could do it too. Another way you could try to do it is to find out how many checks you need to pass. Start a count at 0 for each passport. When the passport passes a check (for example, you see that the eye color is one of the choices) you add +1 to the count. At the end if the count is above a certain number, you know you passed all the checks. Some people might find this more intuitive than running it in reverse.
If something goes wrong it may be difficult to track down where your error is coming in. You may need to check each specific function 1 by 1 to make sure they're behaving properly, because even 1 of them being slightly off will throw off your whole result. What I did was add output that said 'This passport failed due to byr' whenever a passport had an invalid birth year for example. And each other check I used had a similar output so I could always see why a given passport was getting rejected. Though I made sure to turn them off when it was time to run the real data set.
You should also test your individual checks in isolation. I was having trouble figuring out which one of my checks wasn't working correctly, so I took each function on it's own and looked at the examples of valid and invalid results. I put my equation and the value in a separate file and just walked through them until I found the one that wasn't giving me the right result.
You may want to use different methods for different checks. There are a number of different ways you can do each of them, but some are much easier using certain methods. For example, I used regex for hcl and pid, but just a simple comparison for byr. In the same way that you wouldn't eat a steak with a spoon, you can use different tools to make your life easier.
If you use regex, pay close attention to your patterns. They might not be doing exactly what you think they're doing. My biggest time sink ended up being that on of my regex patterns didn't have '^' and '$', but really needed them
I decided to skip python/numpy since this problem seems to be very cleanly solvable in
awk
Part 1 in awk
To run the code
awk -v RS="" -f seven.awk input7.txt
(asuming the awk file is namedseven.awk
)I've run into some problem though with validation in step 2, so looking into that now.Managed to sort it out. While not as short as the solution to step 1 I think it is fairly ok
Part 2 in awk
I kind of like this solution since each rule is tightly encoded next to it's identifier. I had problems getting the end part of each pattern correct until I realized it needed to include both space, newlines and end of lines, i.e the "( |\n|$)".To run the code
awk -v RS="" -f eight.awk input7.txt
(asuming the awk file is namedeightawk
)I like this, I don't know why I didn't think about doing Option<String>. I used just String and checked whether it was empty. I'm going to have to steal this later and update my solution. So much cleaner.
Ruby
Part 1
Part 2
Lost a bunch of time before realizing that I was missing the parens around the first capture group in my height regex, oh well.
I haven't been starting these right on time, and I've got enough actual work I'm behind on that I don't know how long I'll be able to keep up, but AoC is always so much fun. Here's hoping we get more virtual machine puzzles this year!
I ended up having to resort to unit tests to find my validation bugs. Thanks to @Bauke for introducing me to the anyhow crate, which was useful in in the validators.
rust
main.rs
lib.rs
Sheets again! Now, I understand that these look unruly and super ugly...
Basically, I'm splitting everything off and pairing the source row number with it. From there I am pulling a list of all of the blank rows within the dataset. Using a VLOOKUP with
TRUE
I can group everything properly. Blank rows are delimiters, essentially.From there I just clean it up and run some bland REGEX. Not pretty, but it works... much to my surprise. :)
link to sheet
Part 1
Part 2
quick edit: the average height in mine is 7'2"
Well, I'm finished with part 1, and ready to ask for some help on part 2. I've done a deep dive, and still can't come up with the part that's mismatched. I know it's not my input set, because my answer for part 1 was correct, and I currently come up with 128, which it says is "too high." So I filtered my output array to only show the "valid" matches and checked them visually, all of them look correct! So unless I'm missing something glaring, I think maybe my answer is bugged? Please, I don't want an answer outright, just some validation that I'm not insane. Repo (including code for part 2 with debug still included) is here
Part 1
Part 2 (thanks to @spit-evil-olive-tips for the hint!)
It sounds so obvious when you say it like that... :) Thanks very much!
Especially frustrating that this slight change (adding the ^ and % markers to that regex) only removed 1 invalid entry... So my real answer was 127, lol
I had a similar issue, my height regex was missing a capture group and as a result I wasn't actually rejecting two invalid entries. Took a lot of staring to find those two missing parens.
I "solved" part one with python in about 10 minutes... Then spent 30 minutes debugging to find that I was dropping the last passport during parsing... Writing my scheme solution was cathartic. I really need to learn scheme regex because some of my validations (hgt) are pretty messy.
Part 1 & 2
Update:
Scheme regex are weird. I think it's worse
Regex changes
I double pasted my input (why does middle click also paste?!?) and couldn't figure out what was wrong for quite a long time.
I didn't start this one at release time, but it wouldn't have mattered - had a really annoying mistake that took a while to track down. Reasonably happy with the outcome though.
JavaScript
Parts 1 and 2
Fucking regex hell. I'm sure there's some elegant way of doing part 2 but it didn't seem reasonable to generalize this into something nicer. I assume this thread's current top post is the correct answer and it still gives me headaches.
I guess the challenge here was keeping calm and careful, otherwise I don't quite get why we had to validate like 7 fields, just different enough to be annoying. I didn't really enjoy today's.
Part 1+2
Elixir, Day 4
Parts 1 and 2
Rust
Updated solution to incorporate cleaner input reading plus some things I liked from Bauke's (Optionals) and spit-evil-olive-tips' solutions (split on \n\n).
Rust (updated solution)
By the way, would someone better than me at Rust know a cleaner way to do this? Was there a way to do this without clone()ing line to check it twice? Sometimes I feel like I understand the borrow checker but with strings things get messy.
Rust (original solution)
map
, and vectorization okay. More on this after the codeIt's not pretty, but it works, and despite the gymnastics to avoid loops, it runs pretty fast, doing both challenges in ~400 ms.
Part 1
Part 2 diff
This second part was super gross, so I just kind of hope this isn't the challenge we end up building off of for future challenges. If it is, I'll probably do a major refactor of this.
I think after this I will probably use loops just because it's making everything far uglier than necessary. It was a valuable exercise though, I think I may try to approach more things from the "no loops" perspective in order to decide what needs to be broken out into general functions, and then just write those functions with loops. I ended up with a lot of things falling into place quickly at the end of these first few challenges thanks to having broken things out into function earlier on.
I'm stuck on part 1 and can't figure out why it's giving the wrong result.This is what I had
Edit; Working version
Part 1
ok I just realised it's because this design doesn't include the passport until after a blank line meaning the last one is ignored, which ofc happened to be valid.