# Advent of code day 4

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 itdef checkbyr(l):

if "byr" in l:

return True

return Falsedef checkiyr(l):

if "iyr" in l:

return True

return Falsedef checkeyr(l):

if "eyr" in l:

return True

return Falsedef checkhgt(l):

if "hgt" in l:

return True

return Falsedef checkhcl(l):

if "hcl" in l:

return True

return Falsedef checkecl(l):

if "ecl" in l:

return True

return Falsedef checkpid(l):

if "pid" in l:

return True

return Falsevalid = 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.

importre

fp = open("d4input.txt", "r")defcheckbyr(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()

iflen(z) == 4and1920 <= int(z) <= 2002:

return Truecheckiyr(

return False

defl):

# 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()

iflen(z) == 4and2010 <= int(z) <= 2020:

return Truecheckeyr(

return False

defl):

# 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()

iflen(z) == 4and2020 <= int(z) <= 2030:

return Truecheckhgt(

return False

defl):

# 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"iny[0]:

# If cm, the number must be at least 150 and at most 193.

z = zz.split("cm")

if150 <= int(z[0]) <= 193:

return True"in"

ifiny[0]:

# If in, the number must be at least 59 and at most 76.

z = zz.split("in")

if59 <= int(z[0]) <= 76:

return Truecheckhcl(

return False

defl):

# 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)

iflen(z) == 7andz[0] == "#":

return Truecheckecl(

return False

defl):

# 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()

ifzinel:

return Truecheckpid(

return False

defl):

# 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()

iflen(z) == 9:

return Truevalid = 0

return False

passport = []# read through each line add it to the passport list until we get to blank line then check then pause to check the passportdefcheckPassport():

globalvalid

globalpassport

byr =Falseiyr =

Falseeyr =

Falsehgt =

Falsehcl =

Falseecl =

Falsepid =

Falsel

forinpassport:

if notbyr:

byr = checkbyr(l)

if notiyr:

iyr = checkiyr(l)

if noteyr:

eyr = checkeyr(l)

if nothgt:

hgt = checkhgt(l)

if nothcl:

hcl = checkhcl(l)

if notecl:

ecl = checkecl(l)

if notpid:

pid = checkpid(l)

# print(byr,iyr,eyr, hgt, hcl, ecl, pid)

ifbyrandiyrandeyrandhgtandhclandeclandpid:

valid += 1

passport = []defreadLines(text):

globalpassport

while True:

buffer =text.readline()

ifbuffer != "\n":

passport.append(buffer)

ifbuffer == "\n":

checkPassport()

ifbuffer == "":

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.

defcheckbyr(l):

# byr - four digits; at least 1920 and at most 2002.

if"byr"in l:

x =l.split("byr:")

y = x[1].split(" ")

iflen(z) == 4and1920 <= int(z) <= 2002:

return True

return Falsedefcheckbyr(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

iflen(z) == 4and1920 <= 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.