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) -> bool: counter = Counter(password) return policy['lower_bound'] <= counter[policy['char']] <= policy['upper_bound'] 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')