Back Original

Advent of Code 2025 - Day 2 in C#

DISCLOSURE: If you buy through affiliate links, I may earn a small commission. (disclosures)

In this post I'll walk through my solution to AOC 2025 Day 2 in C#.

Part 1

Utilities for Result and Option. I decided I wanted a few utilities to code more like I wanted so reached for OneOf and built a simple version of each.

I also realized I wanted some EnumerableExtensions in the process of solving these:

using OneOf;

public record Success<T>(T Value);
public record Failure<T>(T Error);

public class Result<TSuccess, TError> : OneOfBase<Success<TSuccess>, Failure<TError>>
{
    private Result(OneOf<Success<TSuccess>, Failure<TError>> input) : base(input) { }

    public static implicit operator Result<TSuccess, TError>(Success<TSuccess> success) => new(success);
    public static implicit operator Result<TSuccess, TError>(Failure<TError> failure) => new(failure);
}

public record Some<T>(T Value);
public record None;

public class Option<T> : OneOfBase<Some<T>, None>
{
    private Option(OneOf<Some<T>, None> input) : base(input) { }

    public static implicit operator Option<T>(Some<T> some) => new(some);
    public static implicit operator Option<T>(None none) => new(none);
}

public static class EnumerableExtensions
{
    public static IEnumerable<long> LongRange(long start, long count)
    {
        for (long i = 0; i < count; i++)
            yield return start + i;
    }

    public static IEnumerable<int> RangeStep(int start, int end, int step)
    {
        for (int i = start; i <= end; i += step)
            yield return i;
    }
}

The logic and my notes:

Context: 
* Get a list of ranges that may contain bad product ids
    * START-END
* bad product ids can be identified by going through the range and finding numbers that are repeated 55, 6464

Goal: 
* Find the sum of all the bad numbers

Sub problems: 
* A: Identify bad numbers
    * 1: Turn int to string, use two pointers to see if same
        * if odd length -> no
        * if even try
            * two pointers across
* B: How to iterate ranges
    * 1: Brute force
        * Turn start, end to ints
        * Iterate from start to end
        * See if numbers are bad or not

Solutions: 
* A: Iterate ranges, check for bad - O(ranges * range * check)
    * Iterate ranges - O(ranges)
    * go through range - O(range)
        * check number 
        * if bad, add to accumulator - O(check)

Code:

public static long Day2Part1(string[] lines)
{
    return lines
        .Select(l => l.Split("-").Select(n => long.Parse(n)).ToList())
        .Select(numbers => new NumberRange(Start: numbers[0], End: numbers[1]))
        .Aggregate(0L, (acc, range) =>
            acc + EnumerableExtensions.LongRange(range.Start, range.End - range.Start + 1)
                .Select(n => ParseBadNumber(n))
                .Select(option => option.Match(some => some.Value, none => 0))
                .Sum()
        );
}

public static Option<long> ParseBadNumber(long inputNumber)
{
    var numberString = inputNumber.ToString();
    if(numberString.Length % 2 != 0)
    {
        return new None();
    }

    var midIndex = numberString.Length / 2;
    for(var i = 0; i < midIndex; i++)
    {
        var firstNumber = numberString[i];
        var secondNumber = numberString[i + midIndex];
        if(firstNumber != secondNumber)
        {
            return new None();
        }
    }

    return new Some<long>(inputNumber);
}

Part 2

My notes:

Part 2: 
* Bad numbers are now any number where they are created from a sequence of digits repeated at least twice
* So parseBadNumber makes sense BUT it's not always split in half - it may be more

Subproblems: 
* A: Is this a bad number
    * 0: Brute force matching
        * Foreach substring - check if it matches the other substrings
            * length 1 to length n / 2 - grab substring
            * See if the rest matches it
        * Optimization - only do this if the length is divisible by this substring size - else can't work
    * 1: Loop over numbers the number is divisible by, check substrings against each other

Code:

public static long Day2Part2(string[] lines)
{
    return lines
        .Select(l => l.Split("-").Select(n => long.Parse(n)).ToList())
        .Select(numbers => new NumberRange(Start: numbers[0], End: numbers[1]))
        .Aggregate(0L, (acc, range) =>
            acc + EnumerableExtensions.LongRange(range.Start, range.End - range.Start + 1)
                .Select(n => ParseBadNumber2(n))
                .Select(option => option.Match(some => some.Value, none => 0))
                .Sum()
        );
}

public static Option<long> ParseBadNumber2(long inputNumber)
{
    var numberString = inputNumber.ToString();
    var length = numberString.Length;
    if(length <= 1) return new None();

    var divisibleLengths = Enumerable.Range(1, length-1)
        .Where(n => length % n == 0 && n < length)
        .ToList();

    var isBad = divisibleLengths.Any(divisibleLength =>
        EnumerableExtensions.RangeStep(0, length - 1, divisibleLength)
            .Select(start => numberString.Substring(start, divisibleLength))
            .Distinct()
            .Count() == 1
    );

    return isBad 
        ? new Some<long>(inputNumber)
        : new None();
}

Next

C# wasn't so bad. It was a little clunky and I had to build out some core primitives to code in the way I want but this code really isn't that different than what I might do in F#.

If you liked this post you might also like :