You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
85 lines
2.5 KiB
85 lines
2.5 KiB
from utils import read_input_data
|
|
from collections import Counter
|
|
from typing import List, Dict, Tuple
|
|
|
|
|
|
class TestClass:
|
|
test_data = """1-3 a: abcde
|
|
1-3 b: cdefg
|
|
2-9 c: ccccccccc
|
|
"""
|
|
|
|
def test_count_valid_passwords(self):
|
|
parsed = parse_input_data(self.test_data)
|
|
assert count_valid_passwords(parsed) == 2
|
|
|
|
def test_parse_input_data(self):
|
|
parsed = parse_input_data(self.test_data)
|
|
assert parsed == [
|
|
(
|
|
{
|
|
'lower_bound': 1,
|
|
'upper_bound': 3,
|
|
'char': 'a',
|
|
},
|
|
'abcde'
|
|
),
|
|
(
|
|
{
|
|
'lower_bound': 1,
|
|
'upper_bound': 3,
|
|
'char': 'b',
|
|
},
|
|
'cdefg'
|
|
),
|
|
(
|
|
{
|
|
'lower_bound': 2,
|
|
'upper_bound': 9,
|
|
'char': 'c',
|
|
},
|
|
'ccccccccc'
|
|
),
|
|
]
|
|
|
|
|
|
def parse_input_data(data: str) -> List[Tuple[Dict, str]]:
|
|
lines = [line for line in data.split('\n') if line]
|
|
parsed_passwords = []
|
|
for line in lines:
|
|
policy, password = line.split(':')
|
|
password = password.strip()
|
|
policy_dict = parse_policy(policy)
|
|
parsed_passwords.append((policy_dict, password))
|
|
return parsed_passwords
|
|
|
|
|
|
def parse_policy(input_string: str) -> Dict[str, int]:
|
|
range, char = input_string.split()
|
|
lower_bound, upper_bound = range.split('-')
|
|
return {
|
|
'upper_bound': int(upper_bound),
|
|
'lower_bound': int(lower_bound),
|
|
'char': char,
|
|
}
|
|
|
|
|
|
def verify_password(policy, password, part_two_rules: bool = True) -> bool:
|
|
if not part_two_rules:
|
|
counter = Counter(password)
|
|
return policy['lower_bound'] <= counter[policy['char']] <= policy['upper_bound']
|
|
else:
|
|
lb, ub = policy['lower_bound'], policy['upper_bound']
|
|
# 1 indexed, ^ == XOR
|
|
return (password[lb - 1] == policy['char']) ^ (password[ub - 1] == policy['char'])
|
|
|
|
|
|
def count_valid_passwords(parsed_passwords: List[Tuple[Dict, str]]):
|
|
return sum([verify_password(pol, pwd) for pol, pwd in parsed_passwords])
|
|
|
|
|
|
if __name__ == '__main__':
|
|
raw_data = read_input_data('input.txt')
|
|
parsed_data = parse_input_data(raw_data)
|
|
result = count_valid_passwords(parsed_data)
|
|
print(f'There are {result} valid Passwords, according to their policies')
|
|
|