Refactoring
This commit is contained in:
@@ -39,60 +39,67 @@
|
||||
# Given Santa's current password (your puzzle input), what should his next
|
||||
# password be?
|
||||
|
||||
import re
|
||||
from string import ascii_lowercase
|
||||
|
||||
with open("files/P11.txt", "r") as f:
|
||||
password = [c for c in f.read().strip()]
|
||||
with open("files/P11.txt") as f:
|
||||
password = f.read().strip()
|
||||
|
||||
forbidden_chars = {105, 108, 111}
|
||||
FORBIDDEN_CHARS = {"i", "o", "l"}
|
||||
FORBIDDEN_ASCII = {ord(char) for char in FORBIDDEN_CHARS}
|
||||
|
||||
|
||||
def rule_1(chars: list[str]) -> bool:
|
||||
# increasing straight of at least three letters
|
||||
def rule_1(password: list[str]) -> bool:
|
||||
return any(
|
||||
"".join(chars[i : i + 3]) in ascii_lowercase
|
||||
for i in range(len(chars) - 2)
|
||||
ord(password[i]) + 1 == ord(password[i + 1])
|
||||
and ord(password[i + 1]) + 1 == ord(password[i + 2])
|
||||
for i in range(len(password) - 2)
|
||||
)
|
||||
|
||||
|
||||
def rule_2(chars: list[str], pos: int) -> list[str]:
|
||||
# try with next letter
|
||||
a = ord(chars[pos]) + 1
|
||||
# not contain the letters i, o, or l
|
||||
if a in forbidden_chars:
|
||||
a += 1
|
||||
# wraps around to a
|
||||
if a > 122:
|
||||
a = 97
|
||||
chars[pos] = chr(a)
|
||||
# change now previous letter also
|
||||
rule_2(chars, pos - 1)
|
||||
def rule_2(password: list[str]) -> bool:
|
||||
return not any(char in FORBIDDEN_CHARS for char in password)
|
||||
|
||||
|
||||
def rule_3(password: list[str]) -> bool:
|
||||
pairs = []
|
||||
index = 0
|
||||
while index < len(password) - 1:
|
||||
if password[index] == password[index + 1]:
|
||||
pairs.append(password[index])
|
||||
index += 2
|
||||
else:
|
||||
chars[pos] = chr(a)
|
||||
return chars
|
||||
index += 1
|
||||
return len(set(pairs)) >= 2
|
||||
|
||||
|
||||
def rule_3(chars: list[str]) -> bool:
|
||||
# at least two different, non-overlapping pairs of letters
|
||||
return bool(re.search(r"(\w)\1.*(\w)\2", "".join(chars)))
|
||||
def is_valid_password(password: list[str]) -> bool:
|
||||
return rule_1(password) and rule_2(password) and rule_3(password)
|
||||
|
||||
|
||||
def part_1() -> None:
|
||||
new_password = password
|
||||
found = False
|
||||
while not found:
|
||||
new_password = rule_2(new_password, -1)
|
||||
# rule 1
|
||||
if not rule_1(new_password):
|
||||
continue
|
||||
# rule # 3
|
||||
if not rule_3(new_password):
|
||||
def increment_password(password: list[str]) -> list[str]:
|
||||
updated = password.copy()
|
||||
|
||||
for index in range(len(updated) - 1, -1, -1):
|
||||
next_value = ord(updated[index]) + 1
|
||||
|
||||
while next_value in FORBIDDEN_ASCII:
|
||||
next_value += 1
|
||||
|
||||
if next_value > ord("z"):
|
||||
updated[index] = "a"
|
||||
continue
|
||||
|
||||
res = "".join(new_password)
|
||||
print(f"Santa's next password is {res}")
|
||||
found = True
|
||||
updated[index] = chr(next_value)
|
||||
return updated
|
||||
|
||||
raise ValueError("Password overflowed")
|
||||
|
||||
|
||||
def guess_password(start_password: str) -> str:
|
||||
password = list(start_password)
|
||||
while True:
|
||||
password = increment_password(password)
|
||||
if is_valid_password(password):
|
||||
return "".join(password)
|
||||
|
||||
|
||||
# --- Part Two ---
|
||||
@@ -101,6 +108,9 @@ def part_1() -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
part_1()
|
||||
first_password = guess_password(password)
|
||||
print(f"Santa's next password is {first_password}")
|
||||
|
||||
# Same code again
|
||||
part_1()
|
||||
second_password = guess_password(first_password)
|
||||
print(f"Santa's next password is {second_password}")
|
||||
|
||||
Reference in New Issue
Block a user