Advent of code day 4

Joby Ingram-Dodd
7 min readDec 6, 2020

DAY 1, DAY 2, DAY 3

Photo by Safar Safarov on Unsplash

Welcome to my write up of Day 4 of the Advent of Code the previous days write ups can be at at the links above. Today were were hacking the passport control to make the line go quicker. In the end it was easy enough but it was a timely reminder for me that overlocking some small thing can be hugely time consuming and frustrating.

Part 1

As always we were given a text file as input data, the file had block of text separated by blank lines.

eyr:2028 iyr:2016 byr:1995 ecl:oth
pid:543685203 hcl:#c0946f
hgt:152cm
cid:252

hcl:#733820 hgt:155cm
iyr:2013 byr:1989 pid:728471979
ecl:grn eyr:2022

hgt:171cm
iyr:2013 pid:214368857 hcl:#cfa07d byr:1986 eyr:2028 ecl:grn

Each block represented a passport and included various variables identified by 3 letter codes such as byr (Birth Year) as you can see above. Our task was to work through each ‘passport’ and check if they included all the required variables, counting all the valid passports. Here is the code i used:

fp = open("d4input.txt", "r")
# read through each line untill we get to a blank line which is a new passport
#go through each line and check for required fields
#update counter.
#cid = False #optional so we don''t need to check it
def checkbyr(l):
if "byr" in l:
return True
return False
def checkiyr(l):
if "iyr" in l:
return True
return False
def checkeyr(l):
if "eyr" in l:
return True
return False
def checkhgt(l):
if "hgt" in l:
return True
return False
def checkhcl(l):
if "hcl" in l:
return True
return False
def checkecl(l):
if "ecl" in l:
return True
return False
def checkpid(l):
if "pid" in l:
return True
return False
valid = 0
passport = []
#read through each line add it to the passport lis untill we get to blank line then check then pause to check the passportdef checkPassport():
global valid
global passport
byr = False
iyr = False
eyr = False
hgt = False
hcl = False
ecl = False
pid = False
for l in passport:
if byr == False:
byr = checkbyr(l)
if iyr == False:
iyr = checkiyr(l)
if eyr == False:
eyr = checkeyr(l)
if hgt == False:
hgt = checkhgt(l)
if hcl == False:
hcl = checkhcl(l)
if ecl == False:
ecl = checkecl(l)
if pid == False:
pid = checkpid(l)
#print(byr,iyr,eyr, hgt, hcl, ecl, pid)
if byr and iyr and eyr and hgt and hcl and ecl and pid :
valid += 1
passport = []

def readLines(text):
while True:
buffer = text.readline()
if buffer != "\n":
passport.append(buffer)
if buffer == "\n":
checkPassport()
if buffer == "":
checkPassport()
break
#checkPassport()
readLines(fp)
print(valid)
print(passport)

Let's break it down. Firstly I should say I was trying to be clever here and future proof my code by anticipating that part 2 would be harder and require more checks. So I created a function to check each variable idependtenly. They were all the same and simply used ‘in’.

def checkpid(l):
if "pid" in l:
return True
return False

But i’m jumping ahead, the first task was to split out each block as a passport, so to do this I used.

def readLines(text):
while True:
buffer = text.readline()
if buffer != "\n":
passport.append(buffer)
if buffer == "\n":
checkPassport()
if buffer == "":
checkPassport()
break

This function read through each line in the file and while the line is not blank it adds that line to a list ‘passport’ which represent all the lines of a passport. When we get to a blank line we check the list (‘passport’) to see if it is valid, the list is wiped clean and we repeat the process.

def checkPassport(): 
global valid
global passport
byr = False
iyr = False
eyr = False
hgt = False
hcl = False
ecl = False
pid = False
for l in passport:
if byr == False:
byr = checkbyr(l)
if iyr == False:
iyr = checkiyr(l)
if eyr == False:
eyr = checkeyr(l)
if hgt == False:
hgt = checkhgt(l)
if hcl == False:
hcl = checkhcl(l)
if ecl == False:
ecl = checkecl(l)
if pid == False:
pid = checkpid(l)
#print(byr,iyr,eyr, hgt, hcl, ecl, pid)
if byr and iyr and eyr and hgt and hcl and ecl and pid :
valid += 1
passport = []

The check password function just works through all the smaller functions checking each variable and updates the valid counter as required. It worked really well and seemed pretty efficient code but I’ll admit quite long winded.

A fellow Strive School Luca shared his version of part 1 which is a much tighter method as you can see. You can see his GitHub here.

f = open("day4input.txt", "r")
passports = f.read()


single_passports = passports.split("\n\n")
valid_passport = 0
for i in single_passports:
if "byr" in i and "iyr" in i and "eyr" in i and "hgt" in i and "hcl" in i and "ecl" in i and "pid" in i:
valid_passport += 1
f.close()
print(valid_passport)

Part 2

As anticipated part 2 added in a bunch more complexity, by requiring checks for each parameter to make sure they are withing specific parameters. For example.

  • byr (Birth Year) - four digits; at least 1920 and at most 2002.

Luckly I already had functions for each parameter, so I could keep most of my code the same and simply change each of parameter functions to check the specific requirements and maintain the readability of the code. Here it is.

import re

fp = open("d4input.txt", "r")


def checkbyr(l):
# byr (Birth Year) - four digits; at least 1920 and at most 2002.
if "byr" in l:
x = l.split("byr:")
y = x[1].split(" ")
z = y[0].strip()
if len(z) == 4 and 1920 <= int(z) <= 2002:
return True
return False

def
checkiyr(l):
# iyr (Issue Year) - four digits; at least 2010 and at most 2020.
if "iyr" in l:
x = l.split("iyr:")
y = x[1].split(" ")
z = y[0].strip()
if len(z) == 4 and 2010 <= int(z) <= 2020:
return True
return False

def
checkeyr(l):
# eyr (Expiration Year) - four digits; at least 2020 and at most 2030.
if "eyr" in l:
x = l.split("eyr:")
y = x[1].split(" ")
z = y[0].strip()
if len(z) == 4 and 2020 <= int(z) <= 2030:
return True
return False

def
checkhgt(l):
# hgt (Height) - a number followed by either cm or in:
if "hgt" in l:
x = l.split("hgt:")
y = x[1].split(" ")
zz = y[0].strip()
if "cm" in y[0]:
# If cm, the number must be at least 150 and at most 193.
z = zz.split("cm")
if 150 <= int(z[0]) <= 193:
return True
if
"in" in y[0]:
# If in, the number must be at least 59 and at most 76.
z = zz.split("in")
if 59 <= int(z[0]) <= 76:
return True
return False

def
checkhcl(l):
# hcl (Hair Color) - a # followed by exactly six characters 0-9 or a-f.
if "hcl" in l:
x = l.split("hcl:")
y = x[1].split(" ")
yy = y[0].strip()
z = re.sub('[^a-f0-9#]', '', yy)
if len(z) == 7 and z[0] == "#":
return True
return False

def
checkecl(l):
# ecl (Eye Color) - exactly one of: amb blu brn gry grn hzl oth
el = ["amb", "blu", "brn", "gry", "grn", "hzl", "oth"]
if "ecl" in l:
x = l.split("ecl:")
y = x[1].split(" ")
z = y[0].strip()
if z in el:
return True
return False

def
checkpid(l):
# pid (Passport ID) - a nine-digit number, including leading zeroes.
if "pid" in l:
x = l.split("pid:")
y = x[1].split(" ")
z = y[0].strip()
if len(z) == 9:
return True
return False

valid = 0
passport = []
# read through each line add it to the passport list until we get to blank line then check then pause to check the passportdef checkPassport():
global valid
global passport
byr = False
iyr = False
eyr = False
hgt = False
hcl = False
ecl = False
pid = False
for
l in passport:
if not byr:
byr = checkbyr(l)
if not iyr:
iyr = checkiyr(l)
if not eyr:
eyr = checkeyr(l)
if not hgt:
hgt = checkhgt(l)
if not hcl:
hcl = checkhcl(l)
if not ecl:
ecl = checkecl(l)
if not pid:
pid = checkpid(l)
# print(byr,iyr,eyr, hgt, hcl, ecl, pid)
if byr and iyr and eyr and hgt and hcl and ecl and pid:
valid += 1
passport = []

def readLines(text):
global passport
while True:
buffer = text.readline()
if buffer != "\n":
passport.append(buffer)
if buffer == "\n":
checkPassport()
if buffer == "":
checkPassport()
break



readLines(fp)
print(valid)
print(passport)

For each function I added a comment with the rules and then created them. I was done pretty qucikly, but for some reason it was not working the result I got was wrong. I went back and forwards through the code trying to find the fault, eventually after stepping away for a while, I found the fault.

def checkbyr(l):
# byr - four digits; at least 1920 and at most 2002.
if "byr" in l:
x = l.split("byr:")
y = x[1].split(" ")
if len(z) == 4 and 1920 <= int(z) <= 2002:
return True
return False
def checkbyr(l):
# byr - four digits; at least 1920 and at most 2002.
if "byr" in l:
x = l.split("byr:")
y = x[1].split(" ")
z = y[0].strip() # just in case there are some extra spaces in there
if len(z) == 4 and 1920 <= int(z) <= 2002:
return True
return False

Looking at this one as an example the 2 versions I show only differ by 1 line but it is a crucial line. As I finally figured out, sometimes after I split the line we have some extra spaces at the end, by adding the strip() method I get rid of this spaces and the code works correctly. The reason that the fault was not immediately obvious is because each passport has a different arangements of line and the parameters move around sometimes in the middle of lines sometimes at the ends of lines, meaning we get various combinations of spaces and new lines after the data we actually want. I had to add strip to all the functions.

A fun one today, tomorrow Day 5. Feel free to add comments below.

--

--