from typing import List, Tuple, Set data_structure: type = Tuple[int, int] def parse_data(data: List[str]) -> data_structure: bounds: List[str] = data[0].split('-') return int(bounds[0]), int(bounds[1]) def meets_criteria(value: int, bounds: Tuple[int, int], extra_constraint: bool = False) -> bool: satisfies: bool = 100000 <= value <= 999999 # 6-digit satisfies &= bounds[0] <= value <= bounds[1] # within range digits: List[int] = [10] working_number: int = value has_matching_adjacent_digits: bool = False for i in range(6): digits.append(working_number % 10) working_number = working_number // 10 satisfies &= digits[i + 1] <= digits[i] # non-strictly monotonically increasing digits from left-to-right has_matching_adjacent_digits |= digits[i + 1] == digits[i] satisfies &= has_matching_adjacent_digits # has at least one pair of identical digits if extra_constraint and satisfies: unique_digits: Set[int] = set(digits) has_pair_of_matching_digits: bool = False for digit in unique_digits: has_pair_of_matching_digits |= digits.count(digit) == 2 satisfies &= has_pair_of_matching_digits return satisfies def part1(data: data_structure) -> int: return len([password for password in range(data[0], data[1] + 1) if meets_criteria(password, (data[0], data[1]))]) def part2(data: data_structure) -> int: return len([password for password in range(data[0], data[1] + 1) if meets_criteria(password, (data[0], data[1]), True)]) if __name__ == '__main__': raw_data = ['178416-676461'] print(part1(parse_data(raw_data))) print(part2(parse_data(raw_data)))