> It would be fun to work out mathematically how much they're limiting the password space with these rules.
Alright, you nerd sniped me, but I'm lazy so I just simulated it, this cuts down the password space by ~35%. I didn't take into account passwords with leading 0s in my sampling, but this shouldn't change the result by much.
from collections import Counter
from random import randint
def is_valid(password: str) -> bool:
# cannot have the same number 3 times in a row
if any(password[i] == password[i + 1] == password[i + 2] for i in range(len(password) - 2)):
return False
# cannot have 4 ascending numbers
if any(all(password[i] == str(int(password[i + j]) - j) for j in range(4)) for i in range(len(password) - 3)):
return False
# cannot have 4 descending numbers
if any(all(password[i] == str(int(password[i + j]) + j) for j in range(4)) for i in range(len(password) - 3)):
return False
# cannot have the same number appear more than 5 times
if max(Counter(password).values()) > 5:
return False
# cannot have pairs next to each other if the second pair is one number higher
if any(int(password[i:i+2]) == int(password[i+2:i+4]) - 1 for i in range(len(password) - 3)):
return False
return True
total_valid = 0
samples = 10**5
for i in range(samples):
password = str(randint(10**6, 10**20 - 1))
if is_valid(password):
total_valid += 1
print(f"valid passwords: {total_valid}")
print(f"valid percentage: {total_valid / samples * 100:.2f}%")
valid passwords: 65045valid percentage: 65.05%