I Solved Advent of Code 2025 in Kotlin: Here's How It Went

 

Transcript:

Hello everyone. Are you waiting for the holiday season as much as I do? Is it only because of the holidays? For me, not really. Every year I wait for the holiday season to solve Advent of Code. Have you heard about Advent of Code? Let me quickly introduce it. Advent of Code is an annual series of coding challenges, or coding puzzles if you will, that we solve to the best of our ability. It allows us to learn a lot of things, and in my opinion, learning is one of the most interesting things in life. I can learn new libraries, new algorithms, new languages, whatever. Advent of Code is an amazing excuse to do that. It lets me distract myself from my daily job and switch to solving something unusual.

Hi, my name is Pasha, and I have been solving Advent of Code for more than five years already. Not every year I manage to finish it. Sometimes I don’t have enough time. Sometimes the tasks are just too complex. But this year, and starting from this year, Advent of Code is actually shorter. It is only 12 days instead of 25, which gives me some spare time during December. I would say it gives me more time with my family, which is a nice thing. This is how my Advent of Code main page currently looks. It means that I solved all 12 tasks and earned 24 gold stars. Today we will look at them, and hopefully you will learn something new as well.

Our adventure starts on day one with a task called Secret Entrance. The idea is that we have a code lock with a wheel that we can rotate left or right. Let’s look at the test input. For example, this means we should rotate the wheel 68 ticks to the left, and this one means 60 ticks to the right. On the real input there are many lines, more than 200, and sometimes we rotate the wheel more than 800 times. Our task is to count how many times we stop on zero for part one and how many times we pass zero for part two.

The task of day two is also quite simple. We have to look for repeated patterns in numbers. For example, a number might contain repeating fragments like 101. To solve it, we split the number into halves and generate all possible repeating patterns. The input is given as a set of ranges, for example from one number to another. My approach for part one is straightforward: I generate all possible pattern variations and count how many fall into the allowed ranges. Part two is more interesting, but I reuse the same approach. Now we look for numbers that consist of multiple repetitions of the same pattern, such as 101 repeated several times. Since the number length is fixed, we calculate all possible pattern lengths and generate all valid repetitions. The logic stays the same, only the repetition limits differ.

The visualization for day two is not very exciting because it is mostly numbers. For part one, for example, some ranges contain two valid values, others contain one. For part two, it becomes more interesting because we sum repeated patterns. For instance, a value can be a digit repeated multiple times, or a multi-digit pattern repeated fewer times. This covers the entire day two.

For day one, it is important to know that we start from position 50. We keep a counter and iterate over all input strings. If the string starts with L, we move left, normalize the position if it goes below zero or above 99, and check if we land on zero. For right rotations, we do the same using addition. For part two, we use floor division to determine where we stop and normalize the position again, incrementing the counter for every pass through zero. When we run the program, the result appears immediately for both test and real input. Thanks to Gemini, I implemented visualizations for almost all days. For day one, we can see the wheel rotating, counting how many times it lands on zero and how many times it passes through zero.

Day three is quite straightforward. We have lines of digits, and for part one we need to find the largest possible number without reordering digits. For example, from one line we might get 98, from another 89, and so on. Since reordering is not allowed, the solution is simple. We repeatedly take the largest digit from the allowed prefix. The implementation uses tail recursion. We keep track of the remaining steps, the current sum, and the input list. On each step, we find the maximum digit in the allowed range, update the sum, and move forward. The solution runs extremely fast, around four milliseconds. The visualization shows the process step by step, although it runs much slower visually than the actual computation.

Day four is a typical cellular automaton task. The input is a grid where we remove cells that do not have enough neighbors. For part one, we remove all such cells in one step. For part two, we remove them iteratively until nothing remains. The implementation is simple: count neighbors and repeat. The visualization is the most interesting part here. You can see the grid gradually shrinking, removing large areas first and then smaller tails until everything is gone.

Day five is also not tricky. We have a set of ranges. For part one, we count how many numbers fall into any of the ranges. In Kotlin, this is trivial. For part two, we count how many integers are covered by all ranges combined, without double-counting overlaps. I implemented a function that subtracts one range from another. If a range is fully inside another, it splits it into two. If they do not intersect, it returns the original range. By repeatedly subtracting ranges, we get a list of non-overlapping ranges and sum their lengths. It sounds complex, but it is mostly a set of if-else conditions and runs very fast.

Day seven is my favorite task of the year, mostly because of the visualization, although the task itself is also beautiful. We have a maze shaped like a Christmas tree. A beam starts at a point and splits on splitters. For part one, we count how many times the beam splits. For part two, we count how many beams exist in total, including intersections. Part one is solved with tail recursion. For part two, we add a bit of dynamic programming and track how many beams exist at each step. The visualization shows an exploding number of beams, forming a shape that really looks like a Christmas tree, with splitters acting like decorations.

Day eight is more challenging. We have a set of 3D points and need to connect them by shortest distances. For part one, we connect the closest points until a certain number of connections is reached. For part two, we continue until all points form a single connected component. The algorithm is straightforward: compute all pairwise distances, sort them, and connect points while merging sets. The visualization shows how the shortest connections are formed step by step.

Day nine starts simple but becomes very hard. We are given points in 2D space. For part one, we find the largest rectangle defined by two points, which is easy. For part two, we must find the largest rectangle that fits inside a polygon formed by the points. I used java.awt.Polygon to solve this, checking all possible rectangles and finding the largest one contained within the polygon. The visualization shows the polygon and how candidate rectangles are tested. Gemini helped me generate this solution, which ended up being around 500 lines of code.

Day ten was extremely hard. We have lights and buttons, where each button toggles certain lights. For part one, we need to find a combination of button presses that results in a target configuration. I brute-forced all combinations using lists instead of bitwise operations. For part two, we need to find the minimum number of button presses, which turns the problem into a system of linear equations. I could not solve it manually and ended up using a library called ojAlgo. It allowed me to model the problem as an optimization task and solve it efficiently. I am not proud of the solution itself, but I am happy that I learned a new library. Unfortunately, I did not have time to create a visualization for this day.

Day eleven is more straightforward. We work with a directed graph. For part one, we count all paths from one node to another using a graph library. For part two, we count only paths that pass through specific nodes. Since the graph is acyclic, we can use topological ordering and dynamic programming to count paths efficiently. The visualization shows the graph and how paths propagate through it.

Day twelve is the day I hated the most. We are given shapes and grid sizes, and we need to determine whether the shapes can fit into the grid. I initially wrote a full backtracking solution with rotations and normalization, but for the real input it turned out to be unnecessary. For the actual input, it is enough to compare the total area of the shapes with the grid size. Still, I kept the full solution in the repository. The final result takes about 700 milliseconds to compute. I spent hours on a solution that was mostly not needed, but that is part of the learning experience.

I am thankful to Eric Wastl for creating Advent of Code every year. It is always a lot of fun and a great learning experience. I am also thankful that Google helped me with visualizations, which make everything more enjoyable. If you are like me, please participate in Advent of Code. Ask questions, find solutions, and learn new things. If you liked the video, like it, subscribe to the channel, leave a comment, and see you next time. Pasha out.

Summary

This talk walks through all twelve days of Advent of Code, sharing both the problem ideas and the author’s approaches to solving them. It shows how Advent of Code helps developers learn new algorithms, libraries, and ways of thinking by stepping away from daily work. Several tasks highlight different techniques, from recursion and dynamic programming to graph algorithms, geometry, and linear algebra. Visualizations play a key role in understanding and enjoying many of the solutions. Overall, Advent of Code is presented as a fun and practical way to learn, experiment, and grow as a developer.

Social Media

Videos
card image
Dec 12, 2025
JRush | Container Essentials: Fast Builds, Secure Images, Zero Vulnerabilities

Web-conference for Java developers focused on hands-on strategies for building high-performance containers, eliminating CVEs, and detecting security issues before production.

Videos
card image
Dec 2, 2025
Build RAG System with Spring AI: No More AI Lies

Struggling to find answers in your own documentation? Your LLM is too. Hallucinations happen because models don’t know your data. RAG (Retrieval Augmented Generation) turns unstructured docs into searchable vector embeddings, so your LLM retrieves facts instead of inventing them. This tutorial walks through the full RAG workflow in Spring AI: document ingestion with TikaDocumentReader, embedding generation, vector storage (pgvector, Choma, Milvus, Oracle), and similarity-based retrieval. You’ll build two endpoints: one for uploading documents and one for answering questions strictly using your indexed data. When the system doesn’t know, it says so—no more confident nonsense. Designed for Java teams bringing AI into production systems where accuracy matters. You’ll learn the pattern, get the code, and deploy an LLM that finally stops hallucinating.

Further watching

Videos
card image
Dec 30, 2025
Java in 2025: LTS Release, AI on JVM, Framework Modernization

Java in 2025 isn't about headline features, it's about how production systems changed under the hood. While release notes focus on individual JEPs, the real story is how the platform, frameworks, and tooling evolved to improve stability, performance, and long-term maintainability. In this video, we look at Java from a production perspective. What does Java 25 LTS mean for teams planning to upgrade? How are memory efficiency, startup time, and observability getting better? Why do changes like Scoped Values and AOT optimizations matter beyond benchmarks? We also cover the broader ecosystem: Spring Boot 4 and Framework 7, AI on the JVM with Spring AI and LangChain4j, Kotlin's growing role in backend systems, and tooling updates that make upgrades easier. Finally, we touch on container hardening and why runtime and supply-chain decisions matter just as much as language features.

Videos
card image
Dec 18, 2025
Java 26 Preview: New JEPs and What They Mean for You

Java 26 is the next feature release that brings features for enhanced performance, security, and developer experience. This video discusses the upcoming JDK 26 release, highlighting ten JEPs including JEP 500. JEP 500 focuses on preparing developers for future restrictions on mutating final fields in Java, emphasizing their role in maintaining immutable state. This is crucial for robust programming and understanding the nuances of mutable vs immutable data, especially concerning an immutable class in java. We also touch upon the broader implications for functional programming in Java.

Videos
card image
Dec 12, 2025
Will AI Replace Developers? A Vibe Coding Reality Check 2025

Can AI replace software engineers? ChatGPT, Copilot, and LLM-powered vibe coding tools promise to automate development—but after testing them against 17 years of production experience, the answer is more nuanced than the hype suggests. Full project generation produces over-engineered code that's hard to refactor. AI assistants excel at boilerplate but fail at business logic. MCP servers solve hallucination problems but create context overload. Meanwhile, DevOps automation actually works. This breakdown separates AI capabilities from marketing promises—essential for teams integrating LLMs and copilots without compromising code quality or architectural decisions.