Load the data, sort the two columns, find the absolute difference between them and sum the result.
Code
data = np.sort(load(1, "int"), axis=0)abs(np.diff(data)).sum()
Part 2
numpy has a handy method to find the unique values in an array, and their counts. Let’s use that to make a dictionary of value -> count pairs for the right list with a default value of zero, and then just look up that value for each element of the left list.
Code
lookup = defaultdict(int)lookup.update( {value: count for value, count inzip(*np.unique(data[:, 1], return_counts=True))})sum(x * lookup[x] for x in data[:, 0])
Nothing too complicated going on here: load the data, and find the difference between successive values. To account for decreasing sequences, multiply by the sign of the first difference and then require that all the differences be greater than one and less than or equal to three.
Code
data = load(2, "int")def is_safe(line): diff = np.diff(line) diff = diff * np.sign(diff[0])returnint(((diff >0) & (diff <=3)).all())sum(is_safe(line) for line in data)
Part 2
I spent a bit of time trying to see if there was a neat way of incorporating the “is valid if any one number is deleted” requirement, but I couldn’t immediately see it, so I ended up just iterating over all the possible deletions instead.
Code
total =0for line in data:for idx inrange(len(line)):if is_safe(line[:idx] + line[idx +1 :]): total +=1breaktotal
We get to play with regex! Well, I do – there might be better ways of tackling this. The format for a valid mul instruction is quite strict, encoding that as a regex is fairly straightforward. Once we have that, we can use re.findall to find all the occurrences and extract the integers.
Code
data = load(3, "raw")mul =r"mul\((\d{1,3}),(\d{1,3})\)"sum(int(pair[0]) *int(pair[1]) for pair in re.findall(mul, data))
Part 2:
I’m pretty happy with my part two, which I managed to do fairly elegantly. We can ignore all the sections immediately after a don't() instruction by splitting the string on those, and then discarding the start of each substring up to the first do() instruction. Concatenating all the substrings gives us a clean string with just the segments we are interested in, and we can proceed as before.
Code
clean ="".join( [segment[segment.find("do()") :] for segment in ("do()"+ data).split("don't()")])sum(int(pair[0]) *int(pair[1]) for pair in re.findall(mul, clean))
data = np.array([[ord(char) for char in line] for line in load(4)])mask = np.array([ord(char) for char in"XMAS"])def xmas(chararray):return (chararray == mask).all() or (chararray == mask[::-1]).all()footprints = [np.eye(4), np.fliplr(np.eye(4)), [[1, 1, 1, 1]], [[1], [1], [1], [1]]]sum( scipy.ndimage.generic_filter(data, xmas, footprint=footprint, mode="constant").sum()for footprint in footprints)
Part 2
Code
masks = ["MMASS", "SMASM", "MSAMS", "SSAMM"]encoded_masks = [np.array([ord(char) for char in mask]) for mask in masks]footprint = [[1, 0, 1], [0, 1, 0], [1, 0, 1]]def x_mas(chararray):for mask in encoded_masks:if (chararray == mask).all():return1return0scipy.ndimage.generic_filter(data, x_mas, footprint=footprint, mode="constant").sum()
Source Code
---title: 2024 Solutions---# Imports```{python}# | eval: true# | output: falseimport dataclassesimport functoolsimport itertoolsimport osimport reimport sysfrom collections import defaultdict, deque, namedtuplefrom queue import PriorityQueueimport more_itertoolsimport numpy as npimport pandas as pdimport scipysys.path.insert(1, "..")import utilsload = utils.year_load(2024)```# [Day 1: Historian Hysteria](https://adventofcode.com/2024/day/1)## Part 1Load the data, sort the two columns, find the absolute difference between them and sum the result.```{python}data = np.sort(load(1, "int"), axis=0)abs(np.diff(data)).sum()```## Part 2`numpy` has a handy method to find the unique values in an array, and their counts. Let's use that to make a dictionary of `value -> count` pairs for the right list with a default value of zero, and then just look up that value for each element of the left list.```{python}lookup = defaultdict(int)lookup.update( {value: count for value, count inzip(*np.unique(data[:, 1], return_counts=True))})sum(x * lookup[x] for x in data[:, 0])```# [Day 2: Red-Nosed Reports](https://adventofcode.com/2024/day/2)## Part 1Nothing too complicated going on here: load the data, and find the difference between successive values. To account for decreasing sequences, multiply by the sign of the first difference and then require that all the differences be greater than one and less than or equal to three.```{python}data = load(2, "int")def is_safe(line): diff = np.diff(line) diff = diff * np.sign(diff[0])returnint(((diff >0) & (diff <=3)).all())sum(is_safe(line) for line in data)```## Part 2I spent a bit of time trying to see if there was a neat way of incorporating the "is valid if any one number is deleted" requirement, but I couldn't immediately see it, so I ended up just iterating over all the possible deletions instead.```{python}total =0for line in data:for idx inrange(len(line)):if is_safe(line[:idx] + line[idx +1 :]): total +=1breaktotal```# [Day 3: Mull It Over](https://adventofcode.com/2024/day/3)## Part 1We get to play with `regex`! Well, I do – there might be better ways of tackling this. The format for a valid `mul` instruction is quite strict, encoding that as a regex is fairly straightforward. Once we have that, we can use `re.findall` to find all the occurrences and extract the integers.```{python}data = load(3, "raw")mul =r"mul\((\d{1,3}),(\d{1,3})\)"sum(int(pair[0]) *int(pair[1]) for pair in re.findall(mul, data))```## Part 2:I'm pretty happy with my part two, which I managed to do fairly elegantly. We can ignore all the sections immediately after a `don't()` instruction by splitting the string on those, and then discarding the start of each substring up to the first `do()` instruction. Concatenating all the substrings gives us a clean string with just the segments we are interested in, and we can proceed as before.```{python}clean ="".join( [segment[segment.find("do()") :] for segment in ("do()"+ data).split("don't()")])sum(int(pair[0]) *int(pair[1]) for pair in re.findall(mul, clean))```# [Day 4: Ceres Search](https://adventofcode.com/2024/day/4)## Part 1```{python}data = np.array([[ord(char) for char in line] for line in load(4)])mask = np.array([ord(char) for char in"XMAS"])def xmas(chararray):return (chararray == mask).all() or (chararray == mask[::-1]).all()footprints = [np.eye(4), np.fliplr(np.eye(4)), [[1, 1, 1, 1]], [[1], [1], [1], [1]]]sum( scipy.ndimage.generic_filter(data, xmas, footprint=footprint, mode="constant").sum()for footprint in footprints)```## Part 2```{python}masks = ["MMASS", "SMASM", "MSAMS", "SSAMM"]encoded_masks = [np.array([ord(char) for char in mask]) for mask in masks]footprint = [[1, 0, 1], [0, 1, 0], [1, 0, 1]]def x_mas(chararray):for mask in encoded_masks:if (chararray == mask).all():return1return0scipy.ndimage.generic_filter(data, x_mas, footprint=footprint, mode="constant").sum()```