Advent of Code: 2020 Day 04 solutions
SPOILER ALERT This is a post with my solutions and learnings from the puzzle. Don’t continue reading if you haven’t tried the puzzle on your own yet.
If you want to do the puzzle, visit adventofcode.com/2020/day/4.
My programming language of choice is python
and all examples below are in python.
Key learnings
- Debugging and testing
Todays puzzle has a number of specific requirements to follow. The key learning I took away from it too handle edge cases, follow specification and having a systematic way of debugging. A good practice would to create tests for the edge cases to ensure everything works as expected.
Puzzle
The challenge is to validate data for a thousand passport.
Example input for two 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
They fields are defined as:
- byr (Birth Year)
- iyr (Issue Year)
- eyr (Expiration Year)
- hgt (Height)
- hcl (Hair Color)
- ecl (Eye Color)
- pid (Passport ID)
- cid (Country ID)
Part 1
All fields except for Country ID (cid) are mandatory. Validate that all mandatory fields are present.
Parse input
First step is to save the input in a local file and parse it in python:
# Open the input file
inputfile = open('04.input', 'r')
# Parse lines
data = [x.strip() for x in inputfile.readlines()]
Solution
def part1(data):
valid_passport_count = 0
required_fields = ['byr' ,'iyr' ,'eyr' ,'hgt' ,'hcl' ,'ecl' ,'pid']
# Variable to track number of required fields for current passport
current = 0
for line in data:
if line == '': # Empty line indicates new passport
if current == len(required_fields):
valid_passport_count += 1
current = 0
continue
for field in line.split():
key, val = field.split(':')
if key in required_fields:
current += 1
return valid_passport_count
print "Solution part 1: %d" % part1(data)
Part 2
The second part adds validation for each field. The requirements are:
- 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 0-9 or a-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.
My Solution
This part will have as many variations of solutions as there are developers. The important part is to read carufully the requirements and test against edge-cases. I did my testing continuously by printing relevant variables while coding. The solution below is therefore not the whole “solution” as I see the process as important as the final code.
def valid(passport):
# Validate mandatory fields
fields = ['byr' ,'iyr' ,'eyr' ,'hgt' ,'hcl' ,'ecl' ,'pid']
for f in fields:
if(f not in passport):
return False
# Validate numerical
if not ( 1920 <= int(passport['byr']) <= 2002):
return False
if not ( 2010 <= int(passport['iyr']) <= 2020):
return False
if not ( 2020 <= int(passport['eyr']) <= 2030):
return False
# Validate Height
if 'cm' in passport['hgt'] and not (150 <= int(passport['hgt'][:-2]) <=193):
return False
elif 'in' in passport['hgt'] and not (59 <= int(passport['hgt'][:-2]) <= 76):
return False
if 'cm' not in passport['hgt'] and 'in' not in passport['hgt']:
return False
# Validate strings/enums
if passport['ecl'] not in ['amb', 'blu', 'brn','gry','grn','hzl','oth']:
return False
if re.match(r'^\#[0-9a-f]{6}$', passport['hcl']) is None:
return False
if re.match(r'^\d{9}$', passport['pid']) is None:
return False
return True
def part2(data):
valid_passport_count = 0
current = {}
for line in data:
if line == '':
if valid(current):
valid_passport_count += 1
current = {}
continue
for field in line.split():
field,val = field.split(':')
current[field] = val
return valid_passport_count
Comments
One edge-case I missed was the passport-id with 9 digits. I forgot to add start and end notation on the regex. A 10-digit passport-id was valid as it did in fact contain a 9-digit number. This off by one error was hard for me to find. The lesson learned is to test edge-cases.
Though it is not practical in a “competition” environment. Therefore I tested by using print
continuously to track what got passed and what didn’t.
Thanks for reading!
I hope these solutions were helpful for you. Just ask if anything was hard to grasp.
Complete code can be found at: github.com/cNille/AdventOfCode/blob/master/2020/04.py