Advent of Code 2020: Day 2

Day 2 of Advent of Code 2020 is about validating passwords. It gives you a series of password policies and passwords and then asks you to determine how many of those passwords are valid.

For this challenge, I opted to use no Python libraries and just do text parsing.

!!! SPOILERS AHEAD !!!

You should stop reading if you haven’t solved this puzzle yet!

Part 1

For my solution, I ended up creating two functions. One to verify the password and one to count the number of valid passwords. These could have been combined, but my best coding practices took over and I stopped myself from putting too much into one function.

Here’s the code:

def verify_password(line: str) -> bool:
    policy, password = [item.strip() for item in line.split(':')]
    bounds = policy[:-2]
    letter = policy[-1]
    low, high = [int(item) for item in bounds.split('-')]

    letter_count = password.count(letter)
    if low <= letter_count <= high:
        return True
    return False

def count_good_passwords(data: list) -> int:
    good_passwords = 0
    for line in data:
        if verify_password(line):
            good_passwords += 1
    print(f'Number of good password: {good_passwords}')
    return good_passwords

if __name__ == "__main__":
    data = []
    with open('passwords.txt') as f:
        for line in f:
            data.append(line.strip())
    count_good_passwords(data)

This code will solve the first part of the problem. Here you open the file, extract each line, and strip off the whitespace from both ends. Once you have the list of strings, you pass them on to your function which counts the number of good passwords.

The brains of the operation are in verify_password()which uses a list comprehension to break the line down into a password policy and the password itself. Then you do some checking based on what the policy says and return True if it’s good and False if it’s not.

Part 2

In part 2, the password policy has to be interpreted differently. Rather than repeat the problem statement here, you should go check it out and read how the two differ.

To make this work, I updated the two functions to take in a version argument:

def verify_password(line: str, version: int = 1) -> bool:
    policy, password = [item.strip() for item in line.split(':')]
    bounds = policy[:-2]
    letter = policy[-1]
    low, high = [int(item) for item in bounds.split('-')]

    if version == 1:
        letter_count = password.count(letter)
        if low <= letter_count <= high:
            return True
    elif version == 2:
        letters = [password[low-1], password[high-1]]
        if letters.count(letter) == 1:
            return True
    return False

def count_good_passwords(data: list, version: int = 1) -> int:
    good_passwords = 0
    for line in data:
        if verify_password(line, version):
            good_passwords += 1
    print(f'Number of good password: {good_passwords}')
    return good_passwords

if __name__ == "__main__":
    data = []
    with open('passwords.txt') as f:
        for line in f:
            data.append(line.strip())

    count_good_passwords(data, version=2)

The code is mostly the same except that now it uses a conditional statement to check the password policy differently. The first version counts the letters while the second version checks the letter positioning.

Overall, this is a pretty straight-forward change.

All my code is also on Github and this one includes a couple of unit tests that you can check out.