Advent of Code Day 5

Published December 05, 2025

Ah yes, we've reached day 5 of the 2025 Advent of Code. The first day where attempting a brute force solution will either run out of memory or run until the heat death of the universe, if not both. Conceptually a simple problem but with enough nuance to it to make it interesting. Our input is a list of ranges of unspoiled ingredients and a list of the ingredients we have. For part 1 we need to determine which ingredients from those we have are actually good. This part was easy and did not take me too long to code up. I created a Range class:


@dataclass
class Range:
    start: int
    end: int

    def __contains__(self, item) -> bool:
        return self.start <= item <= self.end

    def __len__(self) -> int:
        return self.end - self.start + 1

    def __lt__(self, other: Range) -> bool:
        return self.start < other.start or self.end < other.end

    def __gt__(self, other: Range) -> bool:
        return self.start > other.start or self.end > other.end

and from there the solution was just to count the number of ingredients that fell into one of the ranges:


return sum(any(ingredient in range_ for range_ in ranges) for ingredient in ingredients)

For part 2 the elves wanted to know how many ingredients, from all of the ingredients in the ranges, were considered to be good. I took a few stabs at this including trying to create a set of all of the ingredients in all of the ranges (it didn't finish running), and multiple attempts with nested loops (they passed the test data and either failed in the real data or didn't finish.) I finally ended up figuring out that my problem was in determining whether or not 2 ranges overlapped. My original check had been:


    def overlap(self, other: Range) -> bool:
        """Determine if there is overlap between 2 ranges"""
        return self.start >= other.start or self.start >= other.end

which led to ranges that didn't actually overlap reporting as if they did. I corrected that to:


    def overlap(self, other: Range) -> bool:
        """Determine if there is overlap between 2 ranges"""
        return self.end >= other.start and self.start <= other.end

and suddenly I got the correct answer. Since I have a feeling that I might need to do similar things in the future (whether this year or in a future one) I put my Range class into my library so I won't have to reinvent the wheel next time I need something like this. Once I had this fixed, I was able to do a pretty simple loop to consolidate the ranges and find the answer:


    for i, range1 in enumerate(ranges):
        if (j := i + 1) >= len(ranges):
            break
        if consolidated := Range.consolidate(range1, ranges[j]):
            ranges[i] = Range(1, 0) 
            ranges[j] = consolidated

Full solution is at https://github.com/brass75/AdventOfCode/blob/main/aoc_2025/day5.py.


Previous: Advent of Code Day 4 Next: Advent of Code Day 6