[Advent of Code 2024](https://adventofcode.com/2024)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import logging
  2. from itertools import pairwise
  3. from sys import stdout
  4. logger = logging.Logger(__name__)
  5. logger_2 = logging.Logger(f"{__name__}_2")
  6. formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
  7. sh = logging.StreamHandler(stdout)
  8. sh.setLevel(logging.INFO)
  9. sh.setFormatter(formatter)
  10. fh = logging.FileHandler("./day02-2.log", mode="w", encoding="utf-8")
  11. fh_2 = logging.FileHandler("./day02-2_round2.log", mode="w", encoding="utf-8")
  12. fh.setLevel(logging.DEBUG)
  13. fh.setFormatter(formatter)
  14. fh_2.setLevel(logging.DEBUG)
  15. fh_2.setFormatter(formatter)
  16. logger.addHandler(sh)
  17. logger.addHandler(fh)
  18. logger_2.addHandler(fh_2)
  19. # So, a report only counts as safe if both of the following are true:
  20. # The levels are either all increasing or all decreasing.
  21. # Any two adjacent levels differ by at least one and at most three.
  22. # The Problem Dampener is a reactor-mounted module that lets the reactor
  23. # safety systems tolerate a single bad level in what would otherwise be a
  24. # safe report. It's like the bad level never happened!
  25. # Now, the same rules apply as before, except if removing a single level
  26. # from an unsafe report would make it safe, the report instead counts as safe.
  27. def report(msg, i=0, spotter=20, use_2=False):
  28. if i % spotter == 0:
  29. logger.info(msg)
  30. else:
  31. logger.debug(msg)
  32. if use_2:
  33. logger_2.debug(msg)
  34. def test_line(line):
  35. pw_l = list(pairwise(line))
  36. raw_distances = [(y-x) for x,y in pw_l]
  37. abs_distances = [abs(x) for x in raw_distances]
  38. # -1 if the pair is going from high to low
  39. # 1 if the pair is going from low to high
  40. # 0 if the pair is equal
  41. directions = [-1 if x > y else (0 if x == y else 1) for x,y in pw_l]
  42. pure_ascending = all([d > 0 for d in directions])
  43. pure_descending = all([d < 0 for d in directions])
  44. pure_and_close = (pure_ascending or pure_descending) and all([1 <= d <= 3 for d in abs_distances])
  45. report(f"""Line {line}:
  46. Pairs: {pw_l}
  47. Raw distances: {raw_distances}
  48. Absolute distances: {abs_distances}
  49. Directions: {directions}
  50. Pure ascending: {pure_ascending}
  51. Pure descending: {pure_descending}
  52. Pure and close: {pure_and_close}""", spotter=100, use_2=True)
  53. return {
  54. "Pairwise": pw_l,
  55. "Raw distances": raw_distances,
  56. "Absolute distances": abs_distances,
  57. "Directions": directions,
  58. "Pure ascending": pure_ascending,
  59. "Pure descending": pure_descending,
  60. "Pure and close": pure_and_close
  61. }
  62. def main():
  63. with open("input02.txt", "r", encoding="utf-8") as f:
  64. lines = [list(map(int,l.split(" "))) for l in f.readlines()]
  65. line_stats = []
  66. safe_lines = 0
  67. for i, line in enumerate(lines):
  68. base_stats = test_line(line)
  69. line_stats.append(base_stats)
  70. if base_stats["Pure and close"]:
  71. safe_lines += 1
  72. continue
  73. break_out = False
  74. j = 0
  75. while not break_out and j < len(line):
  76. new_line = line[:j] + line[j+1:]
  77. report(f"Checking variant of line {line}: {new_line}")
  78. nl_stats = test_line(new_line)
  79. if nl_stats["Pure and close"]:
  80. safe_lines += 1
  81. break_out = True
  82. continue
  83. j += 1
  84. report(f"Number of safe lines: {safe_lines}")
  85. if __name__ == "__main__":
  86. main()