Sean McLemon | Advent of Code

Home | Czech | Blog | GitHub | Advent Of Code | Notes


2015-12-11 - Corporate Policy

(original .ipynb)

Puzzle input is a password. Part one involves finding the next password (i.e. "increment" the letter from the right col) until you get a "valid" password according to the three rules defined in the comments. Part two involves finding the next one after that.

puzzle_input_str = open("./puzzle_input/day11.txt").read()


# Passwords must include one increasing straight of at least three letters, like abc, bcd, cde, and so on, up to xyz. They cannot skip letters; abd doesn't count.
def has_increasing_straight(pwd):
    for i in range(len(pwd)-2):
        if pwd[i]+1 == pwd[i+1] and pwd[i]+2 == pwd[i+2]:
            return True
    return False


# Passwords may not contain the letters i, o, or l, as these letters can be mistaken for other characters and are therefore confusing.
def letter(c):
    n = ord(c)
    assert n >= ord("a") and n <= ord("z")
    return n - ord("a")

letter_a = letter("a")
letter_i = letter("i")
letter_o = letter("o")
letter_l = letter("l")
letter_z = letter("z")
def no_confusing_letters(pwd_digits):
    letters = [d for d in pwd_digits if d == letter_i or d == letter_o or d == letter_l]
    return 0 == len(letters)
    

# Passwords must contain at least two different, non-overlapping pairs of letters, like aa, bb, or zz.
def two_letter_pairs(pwd):
    i = 0
    pairs = 0
    while i < len(pwd) - 1:
        if pwd[i] == pwd[i+1]:
            pairs += 1
            i += 1
        i += 1
    return pairs >= 2
        

def valid_password(pwd_digits):
    return has_increasing_straight(pwd_digits) and no_confusing_letters(pwd_digits) and two_letter_pairs(pwd_digits)


def to_digits(pwd):
    return [letter(c) for c in pwd]


def to_letters(pwd_digits):
    return [chr(ord("a") + c) for c in pwd_digits]


assert not valid_password(to_digits("hijklmmn"))
assert not valid_password(to_digits("abbceffg"))
assert not valid_password(to_digits("abbcegjk"))
assert not valid_password(to_digits("abcdefgh"))
assert     valid_password(to_digits("abcdffaa"))
assert not valid_password(to_digits("ghijklmn"))
assert     valid_password(to_digits("ghjaabcc"))


def next_password(pwd_digits):
    carry = 1
    reversed_next_password = []
    
    for digit in reversed(pwd_digits):
        if carry > 0:
            digit += carry
            if digit > letter_z:
                digit = letter_a
                carry = 1
            else:
                carry = 0
        reversed_next_password.append(digit)
    return list(reversed(reversed_next_password))


def part_one(input_str):
    password = to_digits(input_str)
    while not valid_password(password):
        password = next_password(password)
    
    return "".join(to_letters(password))


assert "abcdffaa" == part_one("abcdefgh")
assert "ghjaabcc" == part_one("ghijklmn")


print("part one:", part_one(puzzle_input_str))
part one: hepxxyzz
def part_two(input_str):
    part_one_password = part_one(input_str)
    next_maybe_password = "".join(to_letters(next_password(to_digits(part_one_password))))
    return part_one(next_maybe_password)

print("part two:", part_two(puzzle_input_str))
part two: heqaabcc