Advent of code day 4
Tags: Advent Of Code, Python3, Strive School, Python

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 passport 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 = []



def readLines(text):

while True:

buffer = text.readline()

if buffer != "

":

passport.append(buffer)

if buffer == "

":

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 != "

":

passport.append(buffer)

if buffer == "

":

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("



")

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 passport 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 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 != "

":

passport.append(buffer)

if buffer == "

":

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.