Advent of Code: 5 mins 1 Bomb solution: Day 4

Overview
Well, you got me Advent. You really got me today. All this S*@t talking on my end and finally something a little more challenging.
Although, I want to admit upfront… I failed. I didn’t get the full two part solution in my designated timeframe. But, you might be happy if you’re a code ๐๏ธโโ๏ธ, because part 1’s solution might have you on your toes๐ฃ .
Today’s answer is written in python. “I know, python again Murph?”. Since I’m going for a low score in code-golf, Swift just wasn’t in the cards today. Maybe I’ll go for C on Monday. But I can promise you, the JVM will not be seen anytime soon ๐คฃ
Enough chatter, let’s get into Day 4 of Advent of code ๐บ ๐ ๐
Problem Statement
Part 1)
“The automatic passport scanners are slow because they’re having trouble detecting which passports have all required fields. The expected fields are as follows:
byr
(Birth Year)iyr
(Issue Year)eyr
(Expiration Year)hgt
(Height)hcl
(Hair Color)ecl
(Eye Color)pid
(Passport ID)cid
(Country ID)
Passport data is validated in batch files (your puzzle input). Each passport is represented as a sequence of key:value
pairs separated by spaces or newlines. Passports are separated by blank lines.
Here is an example batch file containing four passports:
ecl:gry pid:860033327 eyr:2020 hcl:#fffffd byr:1937 iyr:2017 cid:147 hgt:183cm iyr:2013 ecl:amb cid:350 eyr:2023 pid:028048884 hcl:#cfa07d byr:1929 hcl:#ae17e1 iyr:2013 eyr:2024 ecl:brn pid:760753108 byr:1931 hgt:179cm hcl:#cfa07d eyr:2025 pid:166559648 iyr:2011 ecl:brn hgt:59in
The first passport is valid – all eight fields are present. The second passport is invalid – it is missing hgt
(the Height field).
The third passport is interesting; the only missing field is cid
, so it looks like data from North Pole Credentials, not a passport at all! Surely, nobody would mind if you made the system temporarily ignore missing cid
fields. Treat this “passport” as valid.
The fourth passport is missing two fields, cid
and byr
. Missing cid
is fine, but missing any other field is not, so this passport is invalid.
According to the above rules, your improved system would report 2
valid passports.
Count the number of valid passports – those that have all required fields. Treat cid
as optional. In your batch file, how many passports are valid?“
Part 2)
“The line is moving more quickly now, but you overhear airport security talking about how passports with invalid data are getting through. Better add some data validation, quick!
You can continue to ignore the cid
field, but each other field has strict rules about what values are valid for automatic validation:
byr
(Birth Year) – four digits; at least1920
and at most2002
.iyr
(Issue Year) – four digits; at least2010
and at most2020
.eyr
(Expiration Year) – four digits; at least2020
and at most2030
.hgt
(Height) – a number followed by eithercm
orin
:- If
cm
, the number must be at least150
and at most193
. - If
in
, the number must be at least59
and at most76
.
- If
hcl
(Hair Color) – a#
followed by exactly six characters0
–9
ora
–f
.ecl
(Eye Color) – exactly one of:amb
blu
brn
gry
grn
hzl
oth
.pid
(Passport ID) – a nine-digit number, including leading zeroes.cid
(Country ID) – ignored, missing or not.
Your job is to count the passports where all required fields are both present and valid according to the above rules.”
Problem Requirement List
Let’s break down the requirements list for both problems and then see if we can architect a solution that will allow us to reuse as much code as possible between the problems … Again, it’s a very similar problem structure, but the way we validate passports is different. So as long as we take in a validator function, the rest can be reused ๐ป
Part 1 requirements:
- Read in the batched passports and restructure them in a way to make them easy to process
- For each passport:
- Validate the correct passport fields are on the passport
- Sum up the number of correct passports
Part 2 requirements:
- Read-in and process each passport the same way as in part 1
- For each passport:
- Validate the correct passport fields are on the passport
- Validate the values for each field are valid (Mix of regex, using default value sets and number checking)
As you can probably tell, the two questions are nearly identical. The only change between them is that we make the passport validation much more complex than in part 1. Let’s go ahead and write a solution that will allow us to plug and play any passport validator that we want! That way Santa doesn’t run into any trouble with security in the airport ๐บ ๐ ๐
My Solution
My solution is not straightforward at all…In fact, I’m convinced most code-golf solutions aren’t straightforward. But, with a little bit of a deep dive, it can be easy to deconstruct ๐ง .
Now I want to preface this solution with a statement, I pulled the validator function code out of the one line because I’m trying to decouple the code for the validator function. I could’ve easily left it in one line for this blog, but I’m trying to set you up for success in part 2 so cut me some slack, okay?
Processing the Batch
First let’s start with how I handled the input. The real issue is that the input isn’t on one line for each passport. Instead, they’re in batches. So, each passport is represented by the batch of passport requirements in a “batch”. Each passport is then separated by a newline.
We need to then in the file, and make it easier for us to separate the passports. I chose to separate the passports by | signs and maintain the space separation of the passport fields within the passport.
("".join(["|" if line == "\n" else "{l} ".format(l=line.strip()) for line in f.readlines()])).split("|")
Passport Validation
Next, I wrote the functionality that takes each passport (represented by one long string) and transforms it into a dictionary ๐ . I chose to use a dictionary because dict
‘s naturally allow us to choose a field and get its value. Having this functionality is going to be really important for part 2.
dict((field.split(":")[0], field.split(":")[1]) for field in passport.strip().split(" ")) for passport ...(previous code)
Now, all that’s left to do is pass this dictionary to your validator at the security station! Part 1’s validator was very simple, just check to make sure the set of keys available in the dictionary is a superset to the required set of fields. Meaning all fields are available, but there could be extra (who knows, maybe Santa has a checksum field to make sure his data isn’t corrupted).
def validate_passport(passport: dict) -> int: """ Validates a passport dictionary where the keys contain the passport fields and the value of the keys contains the value for the passport returns: 1 if the passport is valid and 0 if invalid """ required_fields = ["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"] return 1 if required_fields.issubset(set(passport.keys())) else 0
My full solution
def validate_passport(passport: dict) -> int: """ Validates a passport dictionary where the keys contain the passport fields and the value of the keys contains the value for the passport returns: 1 if the passport is valid and 0 if invalid """ return 1 if set(["byr", "iyr", "eyr", "hgt", "hcl", "ecl", "pid"]).issubset(set(passport.keys())) else 0 def validate_passports(passports) -> int: return sum([validate_passport(passport) for passport in passports]) # set containing all of the credentialls needed with open("input.txt", 'r') as f: # put all the batches into a line with the requirements seperated by trailing spaces # Each passport batch is sperataed by a pipe valid_passports = validate_passports([dict((field.split(":")[0], field.split(":")[1]) for field in passport.strip().split(" ")) for passport in ("".join(["|" if line == "\n" else "{l} ".format(l=line.strip()) for line in f.readlines()])).split("|")]) print(valid_passports)
Again, as I said, I wrote it all in one line. BUT, I broke it up for your readability.
In fact, you can easily substitute where I called the functions with their bodies. So, I’ll take that as a golf victory for today ๐๏ธโโ๏ธ.
Although, I did fail. I wasn’t able to complete the second part in the amount of time that I set aside for myself. But that’s okay! Because you now have a challenge!
Can you get the solution in under 10 lines? Maybe 5? Let me know down in the comments โฌ๏ธ .
Wrap Up
Hope you enjoyed Day 4 of Advent of Code! Today was a switch up mixed with a bunch of highs and lows ๐ . I won the masters of code golf but failed to finish the second part ๐คฃ . Maybe in the future, I’ll come back and write the solution, but until then, it’s up to you to complete it!
Let me know if you liked the change-up to a code-golf solution. I know it’s not as readable, which makes me want to ๐คฎ when I read code like this in PR’s; but it’s kinda fun in practice. Let me know in the comments โฌ๏ธ if you were able to come up with this (or a more concise) solution on your own! I’m sure there is a better way, but only giving myself 5 minutes to solve it had me in a time crunch!
As always, if you liked todayโs content, make sure you subscribe to the newsletter down below and if you want to support my coffee addiction, help me out by buying me a coffee! It keeps me going to create more AWESOME FREE CONTENT FOR YOU! Thanks for taking the time to unwrap some bytes with me. Cheers! ๐ป ๐ ๐