Metadata
-
Date
-
Tagged
-
Part of series
- Advent of Code 2025 Day 1
- Advent of Code 2025 Day 2
- Advent of Code 2025 Day 3
- Advent of Code 2025 Day 4
- Advent of Code 2025 Day 5
- Advent of Code 2025 Day 6
- Advent of Code 2025 Day 7
- Advent of Code 2025 Day 8
- Advent of Code 2025 Day 9
- Advent of Code 2025 Day 10
- Advent of Code 2025 Day 11
- Advent of Code 2025 Day 12
-
Older post
-
Newer post
Advent of Code 2025 Day 5
Day 5: Cafeteria
https://adventofcode.com/2025/day/5
You arrive at a cafeteria.
The input for today is the cafeteria’s inventory system.
An example input looks like this:
3-510-1416-2012-18
158111732The top block of numbers are inclusive ID ranges for ingredients that are fresh. In this example ingredients with id 3 through 5 are fresh, 10 through 14, etc.
The bottom block are individual IDs that are currently in stock.
The ranges can overlap, and not all ingredients in stock are still fresh.
Parsing
I store the ranges in a list where each item is (start, end).
The ingredient IDs that are in stock are stored in a list too.
fn parse(input: &str) -> (Vec<(u64, u64)>, Vec<u64>) { let (top, bottom) = input.split_once("\n\n").unwrap();
let ranges = top .lines() .map(|line| { let (low, high) = line.split_once('-').unwrap(); (low.parse().unwrap(), high.parse().unwrap()) }) .collect();
let foods = bottom.lines().map(|line| line.parse().unwrap()).collect();
(ranges, foods)}Part 1
The question asks how many of the available ingredient IDs are fresh.
Per ID, I check if it fits into any range, and count the amount of filtered IDs.
fn part_1(input: &str) -> usize { let (ranges, foods) = parse(input);
foods .iter() .filter(|food| ranges.iter().any(|(low, high)| (low..=high).contains(food))) .count()}Part 2
Ignore the ingredients that are currently in stock, consider all ingredients.
The question asks for the number of ingredient IDs that are considered fresh.
The ranges use very large numbers, so bruteforcing by looping over every ID won’t work.
First, I sort the ranges based on their starting number.
I keep track of a single range. Then, loop through the remaining ranges.
- If a new range doesn’t overlap with the current one, I add the amount of IDs in the current range to a total and update the current range.
- It the new range overlaps the current one, I merge them.
This is done by changing the bounds of the current range (eg. turn the 2 ranges
1-5,2-20into the single range1-20).
At the end of the loop, add the IDs that are in the current range.
fn part_2(input: &str) -> u64 { let (mut ranges, _) = parse(input); ranges.sort_unstable_by_key(|range| range.0);
let mut sum = 0; let (mut low, mut high) = ranges[0];
for &(next_low, next_high) in &ranges[1..] { if high < next_low { sum += high - low + 1; (low, high) = (next_low, next_high); } else { // overlap, merge ranges high = high.max(next_high); } } // count last range sum += high - low + 1;
sum}Final code
fn parse(input: &str) -> (Vec<(u64, u64)>, Vec<u64>) { let (top, bottom) = input.split_once("\n\n").unwrap();
let ranges = top .lines() .map(|line| { let (low, high) = line.split_once('-').unwrap(); (low.parse().unwrap(), high.parse().unwrap()) }) .collect();
let foods = bottom.lines().map(|line| line.parse().unwrap()).collect();
(ranges, foods)}
fn part_1(input: &str) -> usize { let (ranges, foods) = parse(input);
foods .iter() .filter(|food| ranges.iter().any(|(low, high)| (low..=high).contains(food))) .count()}
fn part_2(input: &str) -> u64 { let (mut ranges, _) = parse(input); ranges.sort_unstable_by_key(|range| range.0);
let mut sum = 0; let (mut low, mut high) = ranges[0];
for &(next_low, next_high) in &ranges[1..] { if high < next_low { sum += high - low + 1; (low, high) = (next_low, next_high); } else { high = high.max(next_high); } } sum += high - low + 1;
sum}