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
Feb 6, 2026
Backend Developer Roadmap 2026: What You Need to Know

Backend complexity keeps growing, and frameworks can't keep up. In 2026, knowing React or Django isn't enough. You need fundamentals that hold up when systems break, traffic spikes, or your architecture gets rewritten for the third time.I've been building production systems for 15 years. This roadmap covers three areas that separate people who know frameworks from people who can actually architect backend systems: data, architecture, and infrastructure. This is about how to think, not what tools to install.

Videos
card image
Jan 29, 2026
JDBC Connection Pools in Microservices. Why They Break Down (and What to Do Instead)

In this livestream, Catherine is joined by Rogerio Robetti, the founder of Open J Proxy, to discuss why traditional JDBC connection pools break down when teams migrate to microservices, and what is a more efficient and reliable approach to organizing database access with microservice architecture.

Further watching

Videos
card image
Feb 27, 2026
Spring Developer Roadmap 2026: What You Need to Know

Spring Boot is powerful. But knowing the framework isn’t the same as understanding backend engineering. In this video, I walk through the roadmap I believe matters for a Spring developer in 2026. We start with data. That means real SQL — CTEs, window functions, normalization trade-offs — and understanding what ACID and BASE actually imply for system guarantees. Spring Data JPA is useful, but you still need to know what happens underneath. Then architecture: microservices vs modular monolith, serverless, CQRS, and when HTTP, gRPC, Kafka, or WebSockets make sense. Not as buzzwords — but as design choices with trade-offs. Security and infrastructure follow: OWASP Top 10, AuthN vs AuthZ, encryption in transit and at rest, Docker, Kubernetes, Infrastructure as Code, and observability with Micrometer, OpenTelemetry, and Grafana. This roadmap isn’t about mastering every tool. It’s about knowing what affects reliability in production.

Videos
card image
Feb 18, 2026
Build Typed AI Agents in Java with Embabel

Most Java AI demos stop at prompt loops. That doesn't scale in production. In this video, we integrate Embabel into an existing Spring Boot application and build a multi-step, goal-driven agent for incident triage. Instead of manually orchestrating prompt → tool → prompt cycles, we define typed actions and let the agent plan across deterministic and LLM-powered steps. We parse structured input with Ollama, query MongoDB deterministically, classify risk using explicit thresholds, rank affected implants, generate a constrained root cause hypothesis, and produce a bounded containment plan. LLM handles reasoning. Java enforces rules. This is about controlled AI workflows on the JVM — not prompt glue code.

Videos
card image
Feb 12, 2026
Spring Data MongoDB: From Repositories to Aggregations

Spring Data MongoDB breaks down fast once CRUD meets production—real queries, actual data volumes, analytics. What looks simple at first quickly turns into unreadable repository methods, overfetching, and slow queries. In this video, I walk through building a production-style Spring Boot application using Spring Data MongoDB — starting with basic setup and repositories, then moving into indexing, projections, custom queries, and aggregation pipelines. You'll see how MongoDB's document model changes data design compared to SQL, when embedding helps, and when it becomes a liability. We cover where repository method naming stops scaling, how to use @Query safely, when to switch to MongoTemplate, and how to reduce payload size with projections and DTOs. Finally, we implement real MongoDB aggregations to calculate analytics directly in the database and test everything against a real MongoDB instance using Testcontainers. This is not another MongoDB overview. It's a practical guide to actually using Spring Data MongoDB in production without fighting the database.