
Crédit:
The Advent of code is a set of daily programming challenges proposed by E. Wastl during the month of December.
All information here : adventofcode.com/
Disclaimer : all the images in this post are AI generated based on the day story
The Elves have good news and bad news.
The good news is that they've discovered project management! This has given them the tools they need to prevent their usual Christmas emergency. For example, they now know that the North Pole decorations need to be finished soon so that other critical tasks can start on time. The bad news is that they've realized they have a different emergency: according to their resource planning, none of them have any time left to decorate the North Pole!
To save Christmas, the Elves need you to finish decorating the North Pole by December 12th.
- Day 01 : Secret Entrance
- Day 02 : Gift Shop
- Day 03 : Lobby
- Day 04 : Printing Department
- Day 05 : Cafeteria
- Day 06 : Trash Compactor
- Day 07 : Teleporter Hub
- Day 08 : Playground
- Day 09 : Movie Theater
- Day 10 : Factory
- Day 11 : Reactor
- Day 12 : Christmas Trees
Day 1: Secret Entrance



You arrive at the secret entrance to the North Pole base ready to start decorating. Unfortunately, the password seems to have been changed, so you can't get in. A document taped to the wall helpfully explains: "Due to new security protocols, the password is locked in the safe below. Please see the attached document for the new combination."
The safe has a dial with only an arrow on it; around the dial are the numbers 0 through 99 in order. As you turn the dial, it makes a small click noise as it reaches each number.
You're sure that's the right password, but the door won't open. You knock, but nobody answers. You build a snowman while you think.
As you're rolling the snowballs for your snowman, you find another security document that must have fallen into the snow:
"Due to newer security protocols, please use password method 0x434C49434B until further notice."
Day 01 was an appetizer... Simple arithmetic and modulo computation...
The goal is to go left and right a number X of times around a clock which has 100 clicks and compute the number on which you land or the number of times you go past 0.
It means that you could be on the click 90 and needed to "right" (so towards 100) 50 times... As a clock, you will not go to 101 after 100 but to 1... That's where the modulo intervenes and you can use it as such:
1# this ensures that new_click will always be under 100 as
2# the modulo is the remainder of the value on the left when divided by the value on the right
3new_click = (90 + 50) % 100 # = 39 (which is 40 if you start from 0 which is the case in computer world ;-))Task : here (AOC)
Code : here (Github)
Day 2: Gift Shop



You get inside and take the elevator to its only other stop: the gift shop. "Thank you for visiting the North Pole!" gleefully exclaims a nearby sign. You aren't sure who is even allowed to visit the North Pole, but you know you can access the lobby through here, and from there you can access the rest of the North Pole base.
As you make your way through the surprisingly extensive selection, one of the clerks recognizes you and asks for your help. As it turns out, one of the younger Elves was playing on a gift shop computer and managed to add a whole bunch of invalid product IDs to their gift shop database! Surely, it would be no trouble for you to identify the invalid product IDs for them, right?
The clerk quickly discovers that there are still invalid IDs in the ranges in your list. Maybe the young Elf was doing other silly patterns as well?
Day 02, still an easy feature... Some ranges and number comparison...
The goal of the day is to identify invalid ID in specific ranges with specific criteria for valid/invalid IDs :
- Invalid when repeated numbers one after the other
- Invalid when a subset of numbers are repeated X times within the ID
Task : here (AOC)
Code : here (Github)
Day 3: Lobby



You descend a short staircase, enter the surprisingly vast lobby, and are quickly cleared by the security checkpoint. When you get to the main elevators, however, you discover that each one has a red light above it: they're all offline. "Sorry about that," an Elf apologizes as she tinkers with a nearby control panel. "Some kind of electrical surge seems to have fried them. I'll try to get them online soon."
You explain your need to get further underground. "Well, you could at least take the escalator down to the printing department, not that you'd get much further than that without the elevators working. That is, you could if the escalator weren't also offline." "But, don't worry! It's not fried; it just needs power. Maybe you can get it running while I keep working on the elevators."
The escalator doesn't move. The Elf explains that it probably needs more joltage to overcome the static friction of the system and hits the big red "joltage limit safety override" button. You lose count of the number of times she needs to confirm "yes, I'm sure" and decorate the lobby a bit while you wait. Now, you need to make the largest joltage by turning on exactly twelve batteries within each bank.
Day 03, playing with number and ranges and the max function in python...
Within a list of numbers, the goal was to determine the maximum number that could be writtent with numbers from left to right...
The only trick was to ensure to look within the correct slice of the inputs to ensure that you were getting the maximum possible.
Task : here (AOC)
Code : here (Github)
Day 4: Printing Department



You ride the escalator down to the printing department. They're clearly getting ready for Christmas; they have lots of large rolls of paper everywhere, and there's even a massive printer in the corner (to handle the really big print jobs).
Decorating here will be easy: they can make their own decorations. What you really need is a way to get further into the North Pole base while the elevators are offline.
"Actually, maybe we can help with that," one of the Elves replies when you ask for help. "We're pretty sure there's a cafeteria on the other side of the back wall. If we could break through the wall, you'd be able to keep moving. It's too bad all of our forklifts are so busy moving those big rolls of paper around."
If you can optimize the work the forklifts are doing, maybe they would have time to spare to break through the wall.
Now, the Elves just need help accessing as much of the paper as they can.
Once a roll of paper can be accessed by a forklift, it can be removed. Once a roll of paper is removed, the forklifts might be able to access more rolls of paper, which they might also be able to remove. How many total rolls of paper could the Elves remove if they keep repeating this process?
Day 04, first time that we have a coordinated map of elements to go through like:
1..@@.@@@@.
2@@@.@.@.@@
3@@@@@.@.@@
4@.@@@@..@.
5@@.@@@@.@@
6.@@@@@@@.@
7.@.@.@.@@@
8@.@@@.@@@@
9.@@@@@@@@.
10@.@.@@@.@.The goal of the challenge was to identify each position @ with less than four @ around them in the part 1 and in the part 2 to identify how many @ would be accessible if each time a @ was accessible it would be removed leaving place to access others...
Task : here (AOC)
Code : here (Github)
Day 5: Cafeteria



As the forklifts break through the wall, the Elves are delighted to discover that there was a cafeteria on the other side after all.
You can hear a commotion coming from the kitchen. "At this rate, we won't have any time left to put the wreaths up in the dining hall!" Resolute in your quest, you investigate.
"If only we hadn't switched to the new inventory management system right before Christmas!" another Elf exclaims. You ask what's going on.
The Elves in the kitchen explain the situation: because of their complicated new inventory management system, they can't figure out which of their ingredients are fresh and which are spoiled. When you ask how it works, they give you a copy of their database.
The Elves start bringing their spoiled inventory to the trash chute at the back of the kitchen.
So that they can stop bugging you when they get new inventory, the Elves would like to know all of the IDs that the fresh ingredient ID ranges consider to be fresh. An ingredient ID is still considered fresh if it is in any range
Day 05, time to play with ranges and merging them if need be...
The goal was to identify which ingredient was fresh with ensuring that the IDs of the ingredients were existing into defined ranges (which may be overlapping).
The part 2 challenge was to identify all of the ingredient IDs that were fresh in the ranges specified
Task : here (AOC)
Code : here (Github)
Day 6: Trash Compactor



After helping the Elves in the kitchen, you were taking a break and helping them re-enact a movie scene when you over-enthusiastically jumped into the garbage chute!
A brief fall later, you find yourself in a garbage smasher. Unfortunately, the door's been magnetically sealed. While you're trying to figure out how to escape, you are approached by a family of cephalopods! They're pretty sure they can get the door open, but it will take some time.
While you wait, the youngest cephalopod asks for your help with their math assignment. The problems are arranged in a visual puzzle format - columns of numbers with operators at the bottom.
Day 06, time to play with columns, rows and the reduce function for multiplication...
The challenge presents numbers arranged in columns with operators (+ or *) at the bottom. Part 1 requires summing or multiplying each column based on its operator. Part 2 twists things by reading numbers vertically across lines - each digit on a different row forming a single number.
The reduce function from functools is perfect for the multiplication:
1from functools import reduce
2
3# Part 1: process columns with their operators
4cols = [[int(line.split()[i]) for line in lines[:-1]] for i in range(len(lines[0].split()))]
5
6for i, operator in enumerate(lines[-1].split()):
7 if operator == '+':
8 total += sum(cols[i])
9 if operator == '*':
10 total += reduce(lambda tot, x: tot * x, cols[i], 1)Task : here (AOC)
Code : here (Github)
Day 7: Teleporter Hub



After thanking your cephalopod friends, you escape the trash compactor and find yourself in the familiar halls of a North Pole research wing. You discover a facility labeled "teleporter hub" and step onto a large yellow teleporter pad.
You're transported to an unfamiliar room! The room has no doors; the only way out is the teleporter. Unfortunately, the teleporter seems to be leaking magic smoke. Using the diagnostic equipment, you identify error code 0H-N0, indicating an issue with one of the tachyon manifolds.
You locate a diagram of the malfunctioning device and must analyze how tachyon beams interact with it to understand the beam-splitting properties of the tachyon manifold and repair the teleporter system.
Day 07, beam simulation with splitters on a grid...
The challenge involves simulating beams traveling downward through a grid. When a beam hits a splitter (^), it splits into two beams going diagonally left and right. Part 1 counts the total number of splits. Part 2 tracks how many beams end up at each position - since beams can overlap, we need to count them properly.
The key difference between parts is using a set vs a dict:
1# Part 1: just track unique positions with a set
2beams = set([start])
3for row, col in beams:
4 if (nr, col) in splitters:
5 new_beams.add((nr, col-1))
6 new_beams.add((nr, col+1))
7
8# Part 2: track beam counts at each position with a dict
9beams = {start: 1}
10for (row, col), n in beams.items():
11 if (nr, col) in splitters:
12 new_beams[(nr, col-1)] += n # n beams become 2n beams
13 new_beams[(nr, col+1)] += nTask : here (AOC)
Code : here (Github)
Day 8: Playground



The teleporter brings you to an underground space containing a giant playground. A group of Elves are constructing an elaborate Christmas decoration project involving suspended electrical junction boxes.
The Elves need to connect these junction boxes with strings of lights so that electricity can reach every location. But they have limited materials: "To save on string lights, the Elves would like to focus on connecting pairs of junction boxes that are as close together as possible."
They want to understand how their final circuit configuration will look, particularly regarding the sizes of the resulting connected groups.
Day 08, 3D distances and graph connectivity...
This challenge involves junction boxes in 3D space. Part 1 connects the 10 closest pairs and finds the product of the three largest connected circuit sizes. Part 2 builds connections progressively until all boxes form a single circuit - essentially a minimum spanning tree problem.
The 3D Euclidean distance and Union-Find pattern for merging circuits:
1import math
2
3def d(a, b):
4 return math.sqrt((a[0] - b[0])**2 + (a[1]-b[1])**2 + (a[2]-b[2])**2)
5
6# Part 2: merge circuits when connecting boxes
7if all(x is None for x in matched.values()):
8 circuits.append(set([a, b])) # new circuit
9elif matched[a] != matched[b]:
10 # boxes in different circuits - merge them
11 c_a = circuits.pop(mi)
12 c_b = circuits.pop(ma - 1)
13 circuits.append(c_a | c_b)Task : here (AOC)
Code : here (Github)
Day 9: Movie Theater



You slide down a firepole in a corner of the playground and arrive at the North Pole base movie theater!
The Elves are redecorating the theater by replacing tiles in a grid floor pattern. Some tiles are red, and the Elves need to identify the largest possible rectangle using red tiles as opposite corners.
You receive a list of red tile coordinates and must calculate which two tiles form a rectangle with maximum area.
Day 09, geometry with rectangles and point-in-polygon testing...
Part 1 is straightforward: find the two tiles forming the largest rectangle area. Part 2 gets trickier - tiles form edges of rectangles, and you need to find the largest "full" rectangle with no edges crossing through it.
The solution uses the classic ray casting algorithm (odd-even rule) to determine if a point is inside a polygon:
1def area(a, b):
2 return (abs(a[0] - b[0]) + 1) * (abs(a[1] - b[1]) + 1)
3
4def is_inside(p, edges_row, edges_col):
5 # Count edge crossings - odd means inside, even means outside
6 return (sum(1 for x in edges_row[p[1]] if x >= 0 and x < p[0]) % 2 == 1 and
7 sum(1 for y in edges_col[p[0]] if y >= 0 and y < p[1]) % 2 == 1)Task : here (AOC)
Code : here (Github)
Day 10: Factory



You arrive at a large factory where the Elves are unable to operate the machines: "the factory machines are all offline, and none of the Elves can figure out the initialization procedure."
The comedic twist: the instruction manual's section detailing the initialization procedure was eaten by a Shiba Inu! What remains are only partial technical documents - indicator light diagrams, button schematics, and power specifications.
Your task is to decipher how to properly configure the machines' indicator lights using the available documentation fragments.
Day 10, BFS for Part 1 and bringing out the big guns with Z3 solver for Part 2...
Part 1 uses classic BFS to find the minimum steps to reach a target light configuration by toggling lights. Part 2 becomes an optimization problem - finding the minimum number of button presses - where the Z3 SMT solver shines.
1from collections import deque
2from z3 import *
3
4# Part 1: BFS to find minimum steps
5to_visit = deque([(tuple([0 for _ in range(len(target))]), 0)])
6while to_visit:
7 lights, steps = to_visit.popleft()
8 if lights == target:
9 return steps
10 for a in actions:
11 new_lights = tuple([1 - c if i in a else c for i, c in enumerate(lights)])
12 to_visit.append((new_lights, steps + 1))
13
14# Part 2: Z3 optimization
15variables = [Int(f"x{i}") for i in range(len(actions))]
16solver = Optimize()
17for elems, m in equations:
18 solver.add(m == sum([variables[i] for i in elems]))
19solver.minimize(sum(variables))
20solver.check()Task : here (AOC)
Code : here (Github)
Day 11: Reactor



You hear loud beeping coming from a hatch in the floor of the factory. Descending the ladder, you discover a large toroidal reactor powering the facility above.
An Elf urgently explains: "We just installed a new server rack, but we aren't having any luck getting the reactor to communicate with it!" The team believes the issue is triggered by data following some specific path through the devices.
Your task is to trace every path from you to out - starting at a device an Elf hastily labeled "you" and ending at "out," the main reactor output.
Day 11, graph traversal and path counting with memoization...
Part 1 counts all paths from "you" to "out" using BFS with full path tracking. Part 2 adds constraints - count only paths that pass through both "dac" and "fft" nodes. Memoization becomes essential to avoid exponential blowup.
1known = {}
2
3def get_nb_paths(start, finish, dac=False, fft=False):
4 if (start, finish, dac, fft) in known:
5 return known[(start, finish, dac, fft)]
6
7 if start == finish:
8 return 1 if (dac and fft) else 0
9
10 total = 0
11 for n in racks[start]:
12 total += get_nb_paths(n, finish, dac or n == "dac", fft or n == "fft")
13
14 known[(start, finish, dac, fft)] = total
15 return totalTask : here (AOC)
Code : here (Github)
Day 12: Christmas Trees



Racing against time, you discover a ventilation duct that leads to a large, well-lit cavern full of Christmas trees. A group of Elves are working frantically to decorate before the deadline.
They face one significant worry: "the presents for all the young Elves that live here at the North Pole." It's an ancient tradition to put the presents under the trees, but the Elves are worried they won't fit.
The presents come in various unusual shapes and must be placed on a two-dimensional grid - they can be rotated and flipped, but cannot overlap or stack. Can you help fit them all?
Day 12, the grand finale - a classic polyomino packing problem...
This is a constraint satisfaction problem: fit various shapes under trees without overlapping. Shapes can be rotated (4 orientations) and flipped (horizontal/vertical), giving up to 8 possible configurations per shape. The solution uses backtracking with memoization via Python's @cache decorator from functools.
1from functools import cache
2
3# Backtracking with memoization
4@cache
5def is_possible(placements, area, w, h):
6 if all(x is not None for x in placements):
7 return True
8
9 n = placements.index(None)
10 shape = to_fit[n]
11
12 for row in range(h - 3):
13 for col in range(w - 3):
14 for comb in combinations[shape]:
15 if not can_fit(comb, area, w, h, row, col):
16 continue
17 if is_possible(...): # recursive call with updated state
18 return True
19 return False
20
21# Shape transformations: rotate and flip
22def rotate(shape):
23 t = {(0,0): (0,1), (0,1): (0,2), (0,2): (1,2), (1,2): (2,2),
24 (2,2): (2,1), (2,1): (2,0), (2,0): (1,0), (1,0): (0,0), (1,1): (1,1)}
25 return tuple([t[x] for x in shape])
26
27def flip_h(shape):
28 return tuple([(2-r, c) for r, c in shape])Task : here (AOC)
Code : here (Github)