01020304050607080910111213141516171819202122232425

Advent of Code

2024/11

Plutonian Pebbles

in C#

by encse

The ancient civilization on Pluto was known for its ability to manipulate spacetime, and while The Historians explore their infinite corridors, you've noticed a strange set of physics-defying stones.

At first glance, they seem like normal stones: they're arranged in a perfectly straight line, and each stone has a number engraved on it.

Visit the website for the full story and puzzle description.

Today is all about dynamic programming and cached calculations. Our goal is to determine the number of stones based on specific rules derived from the numbers engraved on them. Without careful optimization, this process can quickly spiral out of control.

To address this, I encoded the stone generation logic inside the Eval function and added a cache to prevent exponential growth.

I discovered the ConcurrentDictionary class, which includes a convenient GetOrAdd method. While this functionality is missing in regular Dictionary variants, it allows the caching logic to be neatly encapsulated in a single place. I decided to "abuse" it a bit here, even though my solution doesn’t involve any concurrency at all.

There is an iterative approach to solving this problem as well, which progresses one blink at a time while keeping track of how many times each number occurs at each step. Working through this approach is left as an exercise for the reader.

namespace AdventOfCode.Y2024.Day11;

using System.Linq;

using Cache = System.Collections.Concurrent.ConcurrentDictionary<(string, int), long>;

[ProblemName("Plutonian Pebbles")]
class Solution : Solver {

    public object PartOne(string input) => StoneCount(input, 25);

    public object PartTwo(string input) => StoneCount(input, 75);

    long StoneCount(string input, int blinks) {
        var cache = new Cache();
        return input.Split(" ").Sum(n => Eval(long.Parse(n), blinks, cache));
    }

    // Recursively calculates the total number of stones generated by a single engravement (n)
    // after a specified number of blinks. Uses caching to optimize and prevent exponential
    // computation by storing intermediate results.
    long Eval(long n, int blinks, Cache cache) =>
        cache.GetOrAdd((n.ToString(), blinks), key => 
            key switch {
                (_, 0)   => 1,

                ("0", _) => 
                    Eval(1, blinks - 1, cache),
                
                (var st, _) when st.Length % 2 == 0 =>
                    Eval(long.Parse(st[0..(st.Length / 2)]), blinks - 1, cache) +
                    Eval(long.Parse(st[(st.Length / 2)..]),  blinks - 1, cache),

                _ =>  
                    Eval(2024 * n, blinks - 1, cache)   
            }
        );
}

Please ☆ my repo if you like it!

© 2025 Advent of Code is a registered trademark in the US Images provided by Bing image creator