import logging from itertools import pairwise from sys import stdout logger = logging.Logger(__name__) logger_2 = logging.Logger(f"{__name__}_2") formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') sh = logging.StreamHandler(stdout) sh.setLevel(logging.INFO) sh.setFormatter(formatter) fh = logging.FileHandler("./day02-2.log", mode="w", encoding="utf-8") fh_2 = logging.FileHandler("./day02-2_round2.log", mode="w", encoding="utf-8") fh.setLevel(logging.DEBUG) fh.setFormatter(formatter) fh_2.setLevel(logging.DEBUG) fh_2.setFormatter(formatter) logger.addHandler(sh) logger.addHandler(fh) logger_2.addHandler(fh_2) # So, a report only counts as safe if both of the following are true: # The levels are either all increasing or all decreasing. # Any two adjacent levels differ by at least one and at most three. # The Problem Dampener is a reactor-mounted module that lets the reactor # safety systems tolerate a single bad level in what would otherwise be a # safe report. It's like the bad level never happened! # Now, the same rules apply as before, except if removing a single level # from an unsafe report would make it safe, the report instead counts as safe. def report(msg, i=0, spotter=20, use_2=False): if i % spotter == 0: logger.info(msg) else: logger.debug(msg) if use_2: logger_2.debug(msg) def test_line(line): pw_l = list(pairwise(line)) raw_distances = [(y-x) for x,y in pw_l] abs_distances = [abs(x) for x in raw_distances] # -1 if the pair is going from high to low # 1 if the pair is going from low to high # 0 if the pair is equal directions = [-1 if x > y else (0 if x == y else 1) for x,y in pw_l] pure_ascending = all([d > 0 for d in directions]) pure_descending = all([d < 0 for d in directions]) pure_and_close = (pure_ascending or pure_descending) and all([1 <= d <= 3 for d in abs_distances]) report(f"""Line {line}: Pairs: {pw_l} Raw distances: {raw_distances} Absolute distances: {abs_distances} Directions: {directions} Pure ascending: {pure_ascending} Pure descending: {pure_descending} Pure and close: {pure_and_close}""", spotter=100, use_2=True) return { "Pairwise": pw_l, "Raw distances": raw_distances, "Absolute distances": abs_distances, "Directions": directions, "Pure ascending": pure_ascending, "Pure descending": pure_descending, "Pure and close": pure_and_close } def main(): with open("input02.txt", "r", encoding="utf-8") as f: lines = [list(map(int,l.split(" "))) for l in f.readlines()] line_stats = [] safe_lines = 0 for i, line in enumerate(lines): base_stats = test_line(line) line_stats.append(base_stats) if base_stats["Pure and close"]: safe_lines += 1 continue break_out = False j = 0 while not break_out and j < len(line): new_line = line[:j] + line[j+1:] report(f"Checking variant of line {line}: {new_line}") nl_stats = test_line(new_line) if nl_stats["Pure and close"]: safe_lines += 1 break_out = True continue j += 1 report(f"Number of safe lines: {safe_lines}") if __name__ == "__main__": main()