#!/usr/bin/env python3 """ Counts how many reports are "safe". A report (one line of space-separated integers) is safe if: 1) Levels are strictly monotonic (all increasing OR all decreasing). 2) Adjacent levels differ by at least 1 and at most 3 (inclusive). Usage: python day2/aoc_2024_day2.py input.txt """ import argparse class Report: def __init__(self, levels): self.levels = levels @classmethod def from_line(cls, line): parts = line.split() levels = [] for part in parts: levels.append(int(part)) return cls(levels=levels) def is_safe(self): if len(self.levels) < 2: return True # Determine direction (increasing/decreasing) from the first valid step. # 0 => not decided yet # +1 => increasing # -1 => decreasing direction = 0 for i in range(1, len(self.levels)): previous_level = self.levels[i - 1] current_level = self.levels[i] delta = current_level - previous_level # Adjacent levels must change (no equals). if delta == 0: return False # Differ by at least one and at most three. if abs(delta) > 3: return False step_direction = 1 if delta > 0 else -1 if direction == 0: direction = step_direction # The levels are either all increasing or all decreasing. elif step_direction != direction: return False return True def parse_reports(path): reports = [] with open(path, "r", encoding="utf-8") as f: for raw in f: line = raw.strip() if not line: continue reports.append(Report.from_line(line)) return reports def count_safe(reports): safe_count = 0 for report in reports: if report.is_safe(): safe_count += 1 return safe_count def count_safe_in_file(path): return count_safe(parse_reports(path)) def main(argv=None): parser = argparse.ArgumentParser("CLI to read sample files.") parser.add_argument( "input", help="Input file path.", ) args = parser.parse_args(argv) return count_safe_in_file(args.input) def cli(argv=None): print(main(argv)) return 0 if __name__ == "__main__": raise SystemExit(cli())