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

Christmas Gifts


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
ecl:brn pid:760753108 byr:1931

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 least 1920 and at most 2002.
  • iyr (Issue Year) – four digits; at least 2010 and at most 2020.
  • eyr (Expiration Year) – four digits; at least 2020 and at most 2030.
  • hgt (Height) – a number followed by either cm or in:
    • If cm, the number must be at least 150 and at most 193.
    • If in, the number must be at least 59 and at most 76.
  • hcl (Hair Color) – a # followed by exactly six characters 09 or af.
  • 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("|")])

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! ๐Ÿป ๐ŸŽ„ ๐ŸŽ…

Success! You're on the list.

Shoutout to Mel Poole on Unsplash for the awesome photo!

Leave A Comment