Advent of Code 2025 Day 2: Gift Shop
from advent import parse, positive_ints, Callable
input: tuple[tuple[int, int]] = parse(2, positive_ints, sep=',')
part1 = lambda s: s[: len(s) // 2] * 2 == s
def part2(s: str) -> bool:
length = len(s)
return any(True for i in range(1, (length // 2) + 1) if s[:i] * (length // i) == s)
def solve(invalid: Callable[[str], bool]):
return sum(sum(n for n in range(left, right + 1) if invalid(str(n))) for left, right in input)
assert solve(part1) == 23560874270
assert solve(part2) == 44143124633
Input Parsing
----------------------------------------------------------------------------------------------------
app/day02.txt ➜ 511 chars, 1 lines; first 1 lines:
----------------------------------------------------------------------------------------------------
990244-1009337,5518069-5608946,34273134-34397466,3636295061-3636388848 ... 488-837584,1381-2281,1-19
----------------------------------------------------------------------------------------------------
parse(2) ➜ 35 entries:
----------------------------------------------------------------------------------------------------
((990244, 1009337), (5518069, 5608946), (34273134, 34397466), (3636295 ... ), (1381, 2281), (1, 19))
----------------------------------------------------------------------------------------------------
parse(2, positive_ints, sep=',') provides a list of 2-tuples of (begin, end) for each range.
Solution
We're given a list of ranges of numbers representing product IDs. Our task is to identify invalid
product IDs, and sum them up. So our main function consists of two sum calls. The inner sum computes
the sum of invalid product IDs for one range. The outer sum computes the sum of all the range totals.
def solve(invalid: Callable[[str], bool]):
return sum(
sum(n for n in range(left, right + 1) if invalid(str(n)))
for left, right in input
)
The inner sum uses a predicate, invalid, that is specific to each of the two parts.
For part 1, we test for invalid IDs by testing whether the two halves of the ID are equal.
For part 2, we need to test for IDs that consist solely of repetitions of 1, 2, ... , or N digits.
The largest sequence we need to test for is half the length of the ID, so I simply iterate from 1 up to
that value.
Alternatives
After I completed my solution, I noticed several solutions using a regex technique, and, in hindsight,
I think that's a great approach given we're just looking for patterns in a string. Naturally, that
shortens the code considerably! Here's my regex version:
from advent import parse, positive_ints, Callable, re
input = parse(2, positive_ints, sep=',')
part1 = lambda s: re.match(r'^(\d+)\1$', s)
part2 = lambda s: re.match(r'^(\d+)\1+$', s)
def solve(invalid: Callable[[str], bool]):
return sum(
sum(n for n in range(left, right + 1) if invalid(str(n)))
for left, right in input
)
