| @@ -0,0 +1,35 @@ | |||
| # https://adventofcode.com/2024/day/1 | |||
| # Calculate the sum of the distances between the smallest values in each | |||
| # list, then the second-smallest, then the third-smallest, and so on. | |||
| # The lists are the same length. | |||
| FILENAME = "input01.txt" | |||
| def main(): | |||
| with open(FILENAME, "r") as file: | |||
| lines = [line.strip().split() for line in file.readlines()] | |||
| first_list, second_list = [], [] | |||
| for line in lines: | |||
| first_list.append(int(line[0])) | |||
| second_list.append(int(line[1])) | |||
| sorted_first = sorted(first_list) | |||
| sorted_second = sorted(second_list) | |||
| print(f"""First list: {first_list[:10]}... | |||
| First list sorted: {sorted_first[:10]}... | |||
| Second list: {second_list[:10]}... | |||
| Second list sorted: {sorted_second[:10]}... | |||
| """) | |||
| sum_dists = 0 | |||
| for i, el in enumerate(sorted_first): | |||
| s_el = sorted_second[i] | |||
| dist = abs(el-s_el) | |||
| sum_dists += dist | |||
| if i%5 == 0: | |||
| print(f"Current locations {el} and {s_el}, current distance {dist}, accumulated distance {sum_dists}.") | |||
| print(f"Total accumulated distance: {sum_dists}") | |||
| return sum_dists | |||
| if __name__ == "__main__": | |||
| main() | |||
| @@ -0,0 +1,28 @@ | |||
| # https://adventofcode.com/2024/day/1#part2 | |||
| # Calculate a similarity score by multiplying each value in list 1 | |||
| # by the number of times it appears in list 2. If a value in list 1 | |||
| # doesn't appear in list 2, it contributes 0 to the similarity score. | |||
| FILENAME = "input01.txt" | |||
| def main(): | |||
| with open(FILENAME, "r") as file: | |||
| lines = [line.strip().split() for line in file.readlines()] | |||
| first_list, second_list = [], [] | |||
| for line in lines: | |||
| first_list.append(int(line[0])) | |||
| second_list.append(int(line[1])) | |||
| similarity = 0 | |||
| for i, el in enumerate(first_list): | |||
| el_num = second_list.count(el) | |||
| el_sim = el * el_num | |||
| similarity += el_sim | |||
| if i%5 == 0: | |||
| print(f"Current location {el}, which appears {el_num} times in list 2, current similarity {el_sim}, accumulated similarity {similarity}.") | |||
| print(f"Total accumulated similarity: {similarity}") | |||
| return similarity | |||
| if __name__ == "__main__": | |||
| main() | |||
| @@ -0,0 +1,46 @@ | |||
| import logging | |||
| from itertools import pairwise | |||
| from sys import stdout | |||
| logger = logging.Logger(__name__) | |||
| formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s') | |||
| sh = logging.StreamHandler(stdout) | |||
| sh.setLevel(logging.INFO) | |||
| sh.setFormatter(formatter) | |||
| fh = logging.FileHandler("./day02-1.log", mode="w", encoding="utf-8") | |||
| fh.setLevel(logging.DEBUG) | |||
| fh.setFormatter(formatter) | |||
| logger.addHandler(sh) | |||
| logger.addHandler(fh) | |||
| # 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. | |||
| 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 = [] | |||
| for i, line in enumerate(lines): | |||
| pw_l = list(pairwise(line)) | |||
| ascending = all([x < y for x,y in pw_l]) | |||
| descending = all([x > y for x,y in pw_l]) | |||
| lessthan = all([abs(x - y) <= 3 for x,y in pw_l]) | |||
| safe = (ascending or descending) and lessthan | |||
| line_dict = {"ascending": ascending, "descending": descending, "less than 3": lessthan, "safe": (ascending or descending) and lessthan} | |||
| line_stats.append(line_dict) | |||
| if i % 5 == 0: | |||
| logger.info(f"Spot check: {line} is {'' if safe else 'UN'}safe") | |||
| else: | |||
| logger.debug(f"{line}: {line_dict}") | |||
| print(f"Number of safe lines: {sum([1 for s in line_stats if s['safe']])}") | |||
| if __name__ == "__main__": | |||
| main() | |||
| @@ -0,0 +1,100 @@ | |||
| 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() | |||