# 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() |
# 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() |
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() |
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() |