from typing import Callable, Iterator import sys def check_report_basic(levels: tuple[int, ...]) -> bool: safe = True differences = [next_level - level for level, next_level in zip(levels, levels[1:])] safe = safe and ( all(-3 <= diff <= -1 for diff in differences) or all(1 <= diff <= 3 for diff in differences) ) return safe def check_report_damp(levels: tuple[int, ...]) -> bool: # Obvious solution is using check_report_basic iteratively.. # Is there a better solution? safe = check_report_basic(levels) safe = safe or any(check_report_basic(levels[:i] + levels[i+1:]) for i in range(len(levels))) return safe def tester(data: dict[tuple[int, ...], bool], checker: Callable[[tuple[int, ...]], bool]): for report, expected in data.items(): assert (checker(report) is expected), f"Check failed: {report=} should be {expected=}" def tests(): print("Testing basic check") data = { (7, 6, 4, 2, 1): True, (1, 2, 7, 8, 9): False, (9, 7, 6, 2, 1): False, (1, 3, 2, 4, 5): False, (8, 6, 4, 4, 1): False, (1, 3, 6, 7, 9): True, } tester(data, check_report_basic) print("Basic check passed.") print("Testing dampened check.") data = { (7, 6, 4, 2, 1): True, (1, 2, 7, 8, 9): False, (9, 7, 6, 2, 1): False, (1, 3, 2, 4, 5): True, (8, 6, 4, 4, 1): True, (1, 3, 6, 7, 9): True, } tester(data, check_report_damp) print("Damp check passed.") def main(): tests() filename = sys.argv[1] reports = load_data(filename) total = sum(map(check_report_basic, reports)) print(f"Total safe reports without damping: {total}") reports = load_data(filename) total = sum(map(check_report_damp, reports)) print(f"Total safe reports with damping: {total}") def load_data(filename) -> Iterator[tuple[int, ...]]: with open(filename) as f: for line in f: if line.strip(): yield tuple(map(int, line.split())) if __name__ == '__main__': main()