.---_
/ / /\|
/ / | \ *
/ / \ \
/ / / \ \
./~~~~~~~~~~~\.
( .",^. -". '.~ )
'~~~~~~~~~~~~~'
Lessons from Advent of Code 2023
Leer en español
TL;DR: Advent of Code 2023 in a Nutshell
Advent of Code is a cool coding challenge, but it gets tough fast. If you're new or don't like tricky puzzles, it might be a bit much. However, it's a great chance to boost your coding skills if you're up for a challenge. Just be ready for some frustration, math, and maybe a few laughs or angry moments with naive elves! Give it a shot if you dare!
It's not my first year doing advent of code, but the first one I completed!
You can find my solutions here!
I discovered Advent of Code for the first time in 2021 but I quickly realized it was not a small programming puzzles thing (once you get pass from around day 5, it gets very wild!). In 2021, I gave up after getting only 7 stars! Then in 2022, thanks to the amazing subreddit community I found motivation and energy to get until day 15. After this, I wasn't ready to understand the concepts needed to make a code (or even understand a solution from someone else) to get the answers for day 16 (and let's not even talk about the next days). That's why I am very happy and proud of myself today after getting the 50 * stars of this year 2023!
Advent of Code isn't an easy challenge to complete if you don't have a solid CS & Math background!
That said, I want to share some of the lessons I got from doing Advent of Code 2023. With all the hours I spent doing debugging code, they came some lessons in form of desesperates tries to get solutions new algorithms, approaches to simplify code and use of regular functions and expressions of JavaScript.
I hope this tiny lessons can be in some way helpful for you!
Lesson 1: Parsing data to run in Google Chrome DevTools Console!
As a personal challenge (and later as a way to make it easier for others to try my solutions) I decided to run my AoC into the console tab of Google Chrome DevTools (the Inspection option of your browser that make you feel like a hacker when you know how to use it). So the first thing to do was to find a way to easily parse the data from examples and inputs to manipulate them and solve the challenges.
After 2-3 days, I discovered what will be the most recurrent line of code in my AoC solutions: const data = $0.innerText.split('\n').slice(0,-1);
There are some variations of this line depending on the day's challenge, but in general, it always looks like this.
What's mean $0 in this case? you might ask; and the answer is simple: once you open the Inspection option, the html tag selected could be retrieved in the console with the expression $0. This makes it easy to get the properties of that selected element. Knowing that in the input page, there is only one element (the pre tag with the code tag inside with the text) this expression will always work to get the inner text of the input page.
In case you are curious, my method to get the data before knowing this was literally copying the text into a constant (as a F-crazy caverman!), then when I became an homo sapiens I started using const data = document.getElementsByTagName("pre")[0].innerText.split('\n').slice(0,-1);
With the $0 I evolved into a Homo Sapiens Sapiens =)
.split('\n').slice(0,-1);
become useful thanks to the fact that the examples and input every day keep the same structure: lines of texts separated by a line break each one and a blank space (' ') in the end.
After that, depending on how the data needs to be manipulated, another standard way came to "split" those lines of texts to extract the information. Here is an example from day 13:
Example of data to be parsed:
#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
Example of code to parse that data:
data.forEach((line, i) => {
if (line != '') {
m.push(line);
i === data.length-1 ? mirrors.push(m) : null;
} else {
mirrors.push(m);
m = [];
}
});
Having a constant way to parse data makes it easy to start everyday coding!
Lesson 2: God bless RegEx!
I used to hate regular expressions! (RegEx for friends) I found them very counterintuitive and not human-friendly. After all, in which diabolic language '/\D/g'
this can mean all occurences of non-digit character?! But then when you notice that you can simply write str = item.replace(/\D/g, '');
to turn this abc123def456
into this 123456
instead of writing this:
for (let i = 0; i < line.length; i++) {
if (line[i] === '0' || line[i] === '1' || line[i] === '2' || line[i] === '3'
|| line[i] === '4' || line[i] === '5' || line[i] === '6' || line[i] === '7'
|| line[i] === '8' || line[i] === '9') {
str += line[i];
}
}
you understand the deep beauty of RegEx!
I am still learning how to create my new bestie expressions, while I learn, ChatGPT is such a great mate to help to generate these hellish expressions.
Lesson 3: Ego viam inveniam aut faciam
"I will find a way, or a will create it",that's the translation.
Search path algorithms is something that cannot be missed in your coding repo if you plan to participate in AoC. Last year I learned about Breadth-First Search (BFS for friends) and how to implement it to find the shortest path in a map/graph. During this year, I not only learned how to write variants of this algorithm but also explored some new and interesting ones. But before delving into them, here are some concepts you'd like to know to survive the next paragraph:
- Graph Traversal Algorithm: A graph traversal algorithm is a method for systematically visiting all the vertices (nodes) and edges of a graph.
- Weighted graph: In a weighted graph, each edge has an associated weight or cost, indicating the "cost" of traversing that edge. So if moving from node A to B and then D has a cost of 4 but moving from A to C and then D have a cost of 2, it means that is cheaper to go with the second option.
- Unweighted graph: In an unweighted graph, each edge is considered to have the same weight or cost. It implies that all paths between two nodes are equally "costly" or take the same effort to traverse.
- g-value: In the context of pathfinding algorithms, the g-value represents the actual cost of reaching a particular node from the start node along the current path.
- Heuristic estimate: A heuristic estimate is a function that provides an educated guess or approximation of the remaining cost from a given state to the goal state in a search problem. In the context of pathfinding algorithms, the heuristic estimate is denoted as the h-value. It helps guide the algorithm toward the goal efficiently.
Here is a little brief overview of these beautiful algorithms:
- BFS. This one is particularly useful for finding the shortest path in an unweighted graph. The key point of BFS is that it explores all the vertices at the current level of a queue before moving on to the vertices at the next level. This ensures that the shortest path to each reachable vertex is discovered first. Here is one of my implementations for this algorithm in the day 10 solution:
while (queue.length > 0) { let pipe = queue.shift(); let neighbors = getNeighbors(pipe); for (let i = 0; i < neighbors.length; i++) { let neighbor = neighbors[i]; let contains = visited.some( (v) => v.x === neighbor.x && v.y === neighbor.y ); if (!contains) { visited.push(neighbors[i]); queue.push(neighbors[i]); } } }
- DFS. It is another graph traversal algorithm that explores a graph by visiting as far as possible along each branch before backtracking. Like BFS, DFS is used for graph traversal and can be applied to various graph-related problems.
- Dijikstra. It is a popular and widely used algorithm in CS and graph theory for finding the shortest path between nodes in a graph. Different to BFS, it is more useful with weighted graph. Dijkstra's algorithm relies on the principle of "greediness" by always selecting the node with the smallest tentative distance for exploration. It guarantees that once a node is marked as visited, its tentative distance is the shortest possible.
- A* (pronounced "A star"). It's a popular pathfinding and graph traversal algorithm that combines the principles of Dijkstra's algorithm and greedy best-first search. The key points of A* are the use of both the actual cost from the start node (g-value) and the heuristic estimate to the goal node (h-value) to make informed decisions about which nodes to explore.
I am not doing justice to these algorithms with my poor explanations, so I strongly recommend you search for them in your favorite coding information source to learn more about these powerful coding tools!
Lesson 4: Finding cycles using LCM
Maybe the most interesting discover I did in this AoC year is the possibility of using LCM to identify cycles or repeating patterns. Least Common Multiple (LCM) is a concept of math that for sure you learn during your school years, but probably never used again (and barely remember); it was my case until I found myself stuck on the day 8 part 2 and, searching for salvation solutions, I found out that this concept could be applied to the day challenge to avoid the eternity time that would take a brute force implementation to drop an answer.
A bit of school math: what's LCM?
LCM stands for "Least Common Multiple" is a mathematical concept used to find the smallest positive integer that is a multiple of two or more numbers. Let's see it with a simple example thanks to ChatGPT for save me time with this
To find the least common multiple (LCM) of 4 and 6, you can follow the steps outlined earlier:
- Prime Factorization:
4 = 22 6 = 21 × 3 1
- Collect Prime Factors: in this case, unique prime factors are 2, 3.
- Multiply: LCM(4,6) = 22 × 31 = 4 × 3 = 12
In code, it looks a bit more complicated tho. Here is my implementation in JavaScript:
function findLCM(numbers) {
function findGCD(a, b) {
return b === 0 ? a : findGCD(b, a % b);
}
function findLCMOfTwo(a, b) {
return (a * b) / findGCD(a, b);
}
let lcm = numbers[0];
for (let i = 1; i < numbers.length; i++) {
lcm = findLCMOfTwo(lcm, numbers[i]);
}
return lcm;
}
findLCM([4,6]);
With this implementation, you can pass any quantity of numbers into an [array] and it will return the LCM of those numbers.
Lesson 5: The polygon area formulas
Following with the math you learned but never used, another interesting concept that I learned in this AoC event was how to use Shoelace formula & Pick's theorem to find area and interior points of a polygon.
Knowing the green dots and calculating the red dots in some figure like this can be very useful for different applications. For instance, Geographic Information Systems, Computer-Aided Design (CAD), Games, Robotics and Path Planning.
Would I recommend Advent of Code events?
It's something I have been thinking in the last few days. AoC is definitely a great coding experience to learn, but it can be very frustrating too. Just need to check into the stats to notice it. +220k users got both stars of day 1 but only almost 7k users got both stars of day 25 (in case you don't know, to get the last star, it is required to have all the other stars), so here emerge an interesting question Does this speak this louder about programmer interest in puzzles or AoC difficulty?
In my personal opinion, Advent of Code is not something for novices or people that is not tolerate to failure in an activity for fun. Although the AoC about section point that it is a small programming puzzles event that you can participate without a Computer Science background , the reality is that after the first days, there are not small puzzles and you will need a solid CS & Math knowledge to advance (but hey, it says "participate", not win!). I would not feel comfortable recommend it AoC to around 90% of other programmers that I know. I can say for sure they will desist as soon as days with two-digits start.
That said, Advent of Code is still a great chance to improve coding skills and learn a lot. If you can manage the frustration of debugging code for several hours, implement complicate math concepts into your code and not get angry with elves for being so naive... all only to get a few digitals asterisks like this *, then you should try it! at your own risk
In conclusion, Advent of Code 2023 proved to be a great journey, not only in overcoming coding challenges but also in acquiring invaluable insights and techniques to improve my coding skill. Have you tried AoC? Have you learn something from it? Anything you would like to share about it? If you read till here, I would like to know of you! Feel free to drop me an email: hi@ivanr3d.com if there's anything you would like to share. I would love to enrich this article with more ideas and experiences from other peole.
The Advent of Code 2023 Experience by CP-UPV
Check out this fun video of the experience by the group CP-UPV =D