My 2024 Attempts
Day 1
Part 1
import os
import pandas as pd
input = pd.read_csv(
os.path.join("2024", "day_01", "input_day01.txt"), sep=" ", header=None
)
input.columns = ["l1", "l2"]
def part1(input):
l1 = list(input["l1"])
l1.sort()
l2 = list(input["l2"])
l2.sort()
df_lists = pd.DataFrame({"l1": l1, "l2": l2})
df_lists["diff"] = abs(df_lists["l2"] - df_lists["l1"])
return df_lists["diff"].sum()
print(part1(input))1258579
1.447 seconds elapsed
Part 2
import os
import pandas as pd
input = pd.read_csv(
os.path.join("2024", "day_01", "input_day01.txt"), sep=" ", header=None
)
input.columns = ["l1", "l2"]
def part2(input):
l1 = list(input["l1"])
l2 = list(input["l2"])
count_array = []
for value in l1:
count_array.append(value * l2.count(value))
return sum(count_array)
print(part2(input))23981443
0.025 seconds elapsed
Day 2
Part 1
import os
input = open(os.path.join("2024", "day_02", "input_day02.txt")).read().split("\n")
input = [[int(level) for level in report.split(" ")] for report in input]
def check_if_save(report):
increasing = [report[i + 1] - report[i] for i in range(len(report) - 1)]
if set(increasing) <= {1, 2, 3} or set(increasing) <= {-1, -2, -3}:
return True
else:
return False
def part1(input):
n_safe = sum([check_if_save(report) for report in input])
return n_safe
print(part1(input))213
0.015 seconds elapsed
Part 2
import os
input = open(os.path.join("2024", "day_02", "input_day02.txt")).read().split("\n")
input = [[int(level) for level in report.split(" ")] for report in input]
def check_if_save(report):
increasing = [report[i + 1] - report[i] for i in range(len(report) - 1)]
if set(increasing) <= {1, 2, 3} or set(increasing) <= {-1, -2, -3}:
return True
else:
return False
def part2(input):
n_safe = sum(
[
any(
[
check_if_save(report[:i] + report[i + 1 :])
for i in range(len(report))
]
)
for report in input
]
)
return n_safe
print(part2(input))285
0.032 seconds elapsed
Day 3
Part 1
import os
import re
import math
input = open(os.path.join("2024", "day_03", "input_day03.txt")).read()
def get_multiplications(input):
mul_strings = re.findall(r"mul\(\d+,\d+\)", input)
mul_vals = [[int(s) for s in re.findall(r"\d+", mul)] for mul in mul_strings]
mults = [math.prod(x) for x in mul_vals]
mult_sum = sum(mults)
return mult_sum
def part1(input):
mult_sum = get_multiplications(input)
return mult_sum
print(part1(input))167090022
0.011 seconds elapsed
Part 2
import os
import re
import math
input = open(os.path.join("2024", "day_03", "input_day03.txt")).read()
def get_multiplications(input):
mul_strings = re.findall(r"mul\(\d+,\d+\)", input)
mul_vals = [[int(s) for s in re.findall(r"\d+", mul)] for mul in mul_strings]
mults = [math.prod(x) for x in mul_vals]
mult_sum = sum(mults)
return mult_sum
def part2(input):
input_cleaned = " ".join(sec.split("don't()")[0] for sec in input.split("do()"))
mult_sum = get_multiplications(input_cleaned)
return mult_sum
print(part2(input))89823704
0.009 seconds elapsed
Day 4
Part 1
import os
import re
input = open(os.path.join("2024", "day_04", "input_day04.txt")).read()
input = input.split("\n")
def match(matrix, pattern, width):
matches = 0
for i in range(len(matrix) - width + 1):
for j in range(len(matrix[i]) - width + 1):
block = "".join(matrix[i + x][j : j + width] for x in range(width))
matches += bool(re.match(pattern, block))
return matches
def part1(input):
word_n = 0
for rotation in range(4):
word_n += sum(row.count("XMAS") for row in input)
word_n += match(input, r"X.{4}M.{4}A.{4}S", 4)
input = ["".join(row[::-1]) for row in zip(*input)]
return word_n
print(part1(input))2591
0.139 seconds elapsed
Part 2
import os
import re
input = open(os.path.join("2024", "day_04", "input_day04.txt")).read()
input = input.split("\n")
def match(matrix, pattern, width):
matches = 0
for i in range(len(matrix) - width + 1):
for j in range(len(matrix[i]) - width + 1):
block = "".join(matrix[i + x][j : j + width] for x in range(width))
matches += bool(re.match(pattern, block))
return matches
def part2(input):
word_n = 0
for rotation in range(4):
word_n += match(input, r"M.M.A.S.S", 3)
input = ["".join(row[::-1]) for row in zip(*input)]
return word_n
print(part2(input))1880
0.132 seconds elapsed
Day 5
Day 6
Day 7
Part 1
import os
import re
from functools import reduce
from itertools import product
from operator import add, mul
input = open(os.path.join("2024", "day_07", "input_day07.txt")).read()
input = [list(map(int, re.findall(r"(\d+)", x))) for x in input.splitlines()]
def check_equation(equation, operators):
test_val, nums = equation[0], equation[1:]
for ops in product(operators, repeat=(len(nums) - 1)):
if reduce(lambda k, x: x[0](k, x[1]), zip(ops, nums[1:]), nums[0]) == test_val:
return True
return False
def part1(input):
cal_result = sum(eq[0] for eq in input if check_equation(eq, (add, mul)))
return cal_result
print(part1(input))7885693428401
0.402 seconds elapsed
Part 2
import os
import re
from functools import reduce
from itertools import product
from operator import add, mul
input = open(os.path.join("2024", "day_07", "input_day07.txt")).read()
input = [list(map(int, re.findall(r"(\d+)", x))) for x in input.splitlines()]
def check_equation(equation, operators):
test_val, nums = equation[0], equation[1:]
for ops in product(operators, repeat=(len(nums) - 1)):
if reduce(lambda k, x: x[0](k, x[1]), zip(ops, nums[1:]), nums[0]) == test_val:
return True
return False
def _concat(a, b):
a *= 10 ** len(str(b))
return a + b
def part2(input):
cal_result = sum(eq[0] for eq in input if check_equation(eq, (add, mul, _concat)))
return cal_result
print(part2(input))348360680516005
29.536 seconds elapsed
Day 8
Part 1
import os
input = open(os.path.join("2024", "day_08", "input_day08.txt")).read().splitlines()
input = [list(line) for line in input]
def get_pair_antinodes_pt1(antinodes, n, m, x_i, y_i, x_j, y_j):
dx = x_j - x_i
dy = y_j - y_i
if x_i - dx >= 0 and y_i - dy >= 0 and y_i - dy < m:
antinodes.add((x_i - dx, y_i - dy))
if x_j + dx < n and y_j + dy < m and y_j + dy >= 0:
antinodes.add((x_j + dx, y_j + dy))
def part1(input):
n = len(input)
m = len(input[0])
antinodes = set()
antenna_locations = {}
for i in range(n):
for j in range(m):
if input[i][j] != ".":
c = input[i][j]
antenna_locations[c] = antenna_locations.get(c, []) + [(i, j)]
for freq, antenna in antenna_locations.items():
if len(antenna) < 2:
continue
for i in range(len(antenna) - 1):
for j in range(i + 1, len(antenna)):
x_i, y_i = antenna[i]
x_j, y_j = antenna[j]
get_pair_antinodes_pt1(antinodes, n, m, x_i, y_i, x_j, y_j)
return len(antinodes)
print(part1(input))308
0.013 seconds elapsed
Part 2
import os
input = open(os.path.join("2024", "day_08", "input_day08.txt")).read().splitlines()
input = [list(line) for line in input]
def get_pair_antinodes_pt2(antinodes, n, m, x_i, y_i, x_j, y_j):
dx = x_j - x_i
dy = y_j - y_i
multiplier = 0
while (
x_i - multiplier * dx >= 0
and y_i - multiplier * dy >= 0
and y_i - multiplier * dy < m
):
antinodes_new = antinodes.add((x_i - multiplier * dx, y_i - multiplier * dy))
multiplier += 1
multiplier = 0
while (
x_j + multiplier * dx < n
and y_j + multiplier * dy < m
and y_j + multiplier * dy >= 0
):
antinodes_new = antinodes.add((x_j + multiplier * dx, y_j + multiplier * dy))
multiplier += 1
return antinodes_new
def part2(input):
n = len(input)
m = len(input[0])
antinodes = set()
antenna_locations = {}
for i in range(n):
for j in range(m):
if input[i][j] != ".":
c = input[i][j]
antenna_locations[c] = antenna_locations.get(c, []) + [(i, j)]
for freq, antenna in antenna_locations.items():
if len(antenna) < 2:
continue
for i in range(len(antenna) - 1):
for j in range(i + 1, len(antenna)):
x_i, y_i = antenna[i]
x_j, y_j = antenna[j]
get_pair_antinodes_pt2(antinodes, n, m, x_i, y_i, x_j, y_j)
return len(antinodes)
print(part2(input))1147
0.013 seconds elapsed
Day 9
Day 10
Day 11
Part 1
import os
from functools import cache
input = open(os.path.join("2024", "day_11", "input_day11.txt")).read().split()
@cache
def calculate_state(n):
if n == 0:
return [1]
string_num = str(n)
if len(string_num) % 2 == 0:
mid = len(string_num) // 2
return [int(string_num[:mid]), int(string_num[mid:])]
new_state = [n * 2024]
return new_state
@cache
def stone_count(stone, blinks_left):
if blinks_left == 0:
return 1
new_stone_state = calculate_state(stone)
new_stone_count = sum(stone_count(num, blinks_left - 1) for num in new_stone_state)
return new_stone_count
def part1(input):
n_blinks = 25
total_stones = 0
stones = list(int(stone) for stone in input)
for stone in stones:
total_stones += stone_count(stone, n_blinks)
return total_stones
print(part1(input))189547
0.017 seconds elapsed
Part 2
import os
from functools import cache
input = open(os.path.join("2024", "day_11", "input_day11.txt")).read().split()
@cache
def calculate_state(n):
if n == 0:
return [1]
string_num = str(n)
if len(string_num) % 2 == 0:
mid = len(string_num) // 2
return [int(string_num[:mid]), int(string_num[mid:])]
new_state = [n * 2024]
return new_state
@cache
def stone_count(stone, blinks_left):
if blinks_left == 0:
return 1
new_stone_state = calculate_state(stone)
new_stone_count = sum(stone_count(num, blinks_left - 1) for num in new_stone_state)
return new_stone_count
def part2(input):
n_blinks = 75
stones = list(int(stone) for stone in input)
total_stones = 0
for stone in stones:
total_stones += stone_count(stone, n_blinks)
return total_stones
print(part2(input))224577979481346
0.305 seconds elapsed
Day 12
Day 13
Day 14
Part 1
library(tidyverse)
input <- read_delim(
here::here("2024", "day_14", "input_day14.txt"),
delim = " ",
col_names = c("pos", "vel")
)
input <- input |>
mutate(
starts = str_extract_all(pos, "\\d+"),
velocities = str_extract_all(vel, "-?\\d+")
)
starts <- input |>
pull(starts) |>
map(as.numeric)
velocities <- input |>
pull(velocities) |>
map(as.numeric)
dims <- c(101, 103)
safety_factor <- function(starts, velocities, dims, times) {
end_pos <- map2(
starts,
velocities,
\(x, y, dims, times) (x + y * times) %% dims,
dims = dims,
times = times
)
do.call(rbind, end_pos) |>
as_tibble(.name_repair = "unique") |>
rename(x = "...1", y = "...2")
}
safety_factor(starts, velocities, dims, times = 100) |>
mutate(
quadrant = case_when(
x < (dims[1] - 1) / 2 & y < (dims[2] - 1) / 2 ~ 1,
x < (dims[1] - 1) / 2 & y > (dims[2] - 1) / 2 ~ 2,
x > (dims[1] - 1) / 2 & y < (dims[2] - 1) / 2 ~ 3,
x > (dims[1] - 1) / 2 & y > (dims[2] - 1) / 2 ~ 4,
)
) |>
filter(!is.na(quadrant)) |>
count(quadrant) |>
pull(n) |>
prod() |>
print()[1] 215987200
0.441 sec elapsed
Part 2
library(tidyverse)
input <- read_delim(
here::here("2024", "day_14", "input_day14.txt"),
delim = " ",
col_names = c("pos", "vel")
)
input <- input |>
mutate(
starts = str_extract_all(pos, "\\d+"),
velocities = str_extract_all(vel, "-?\\d+")
)
starts <- input |>
pull(starts) |>
map(as.numeric)
velocities <- input |>
pull(velocities) |>
map(as.numeric)
dims <- c(101, 103)
second_range <- seq(8000, 8100)
safety_factors <- map(
second_range,
~ safety_factor(starts, velocities, dims, times = .x) |>
mutate(
second = .x,
quadrant = case_when(
x < (dims[1] - 1) / 2 & y < (dims[2] - 1) / 2 ~ 1,
x < (dims[1] - 1) / 2 & y > (dims[2] - 1) / 2 ~ 2,
x > (dims[1] - 1) / 2 & y < (dims[2] - 1) / 2 ~ 3,
x > (dims[1] - 1) / 2 & y > (dims[2] - 1) / 2 ~ 4,
)
)
) |>
bind_rows()
min_iterations <- safety_factors |>
group_by(second) |>
summarise(
var_x = var(x),
var_y = var(y)
) |>
arrange(var_x, var_y) |>
slice_head(n = 1) |>
pull(second)
print(min_iterations)
safety_factors |>
filter(second == min_iterations) |>
ggplot(aes(x = x, y = y)) +
geom_tile(fill = "white", color = "grey80", size = 0.1) +
scale_y_reverse() +
coord_equal() +
theme_void() +
theme(
plot.margin = margin(rep(2, 4), unit = "mm"),
plot.background = element_rect(
fill = "#0f0f23",
color = "transparent"
)
)
ggsave(
here::here("2024", "day_14", "plot_day14_pt2.png"),
last_plot(),
height = 5, width = 10, dpi = 300, bg = "transparent"
)[1] 8050
4.488 sec elapsed

Day 15
Part 1
import os
input = open(os.path.join("2024", "day_15", "input_day15.txt")).read().split("\n\n")
def move_coordinates(coord1, coord2):
new_coords = tuple(i + j for i, j in zip(coord1, coord2))
return new_coords
def part1(input):
grid = input[0].splitlines()
moves = input[1].replace("\n", "")
boxes = {
(i, j)
for i, line in enumerate(grid)
for j, char in enumerate(line)
if char == "O"
}
walls = {
(i, j)
for i, line in enumerate(grid)
for j, char in enumerate(line)
if char == "#"
}
robot = [
(i, j)
for i, line in enumerate(grid)
for j, char in enumerate(line)
if char == "@"
]
assert len(robot) == 1
robot = robot[0]
move_dict = {"<": (0, -1), "^": (-1, 0), "v": (1, 0), ">": (0, 1)}
for move in moves:
direction = move_dict[move]
target1 = move_coordinates(robot, direction)
if target1 not in walls and target1 not in boxes:
robot = target1
else:
target2 = target1
while target2 in boxes:
target2 = move_coordinates(target2, direction)
if target2 in walls:
continue
else:
robot = target1
boxes.remove(target1)
boxes.add(target2)
answer = sum(100 * i + j for i, j in boxes)
return answer
print(part1(input))1415498
0.046 seconds elapsed