Day 5: Cafeteria
Megathread guidelines
- Keep top level comments as only solutions, if you want to say something other than a solution put it in a new post. (replies to comments can be whatever)
- You can send code in code blocks by using three backticks, the code, and then three backticks or use something such as https://topaz.github.io/paste/ if you prefer sending it through a URL
FAQ
- What is this?: Here is a post with a large amount of details: https://programming.dev/post/6637268
- Where do I participate?: https://adventofcode.com/
- Is there a leaderboard for the community?: We have a programming.dev leaderboard with the info on how to join in this post: https://programming.dev/post/6631465


Rust
I didn’t look at the input at first so tried a naive version with sets, but it was too slow even for part one. I got smarter and wrote a version with less code that runs in under half a millisecond.
type Id = usize; struct Ingredients { fresh: Vec<RangeInclusive<Id>>, available: HashSet<Id>, } impl Ingredients { fn is_fresh(&self, id: Id) -> bool { self.fresh.iter().any(|range| range.contains(&id)) } fn num_fresh_available(&self) -> usize { self.available .iter() .filter(|&&id| self.is_fresh(id)) .count() } fn num_fresh_all(&self) -> usize { self.fresh .iter() .map(|range| 1 + range.end() - range.start()) .sum() } } fn merge_ranges(mut ranges: Vec<RangeInclusive<Id>>) -> Vec<RangeInclusive<Id>> { if ranges.is_empty() { return ranges; } ranges.sort_by_key(|range| *range.start()); let mut merged = Vec::new(); let mut start = ranges[0].start(); let mut end = ranges[0].end(); for range in ranges.iter().skip(1) { if range.start() > end { merged.push(RangeInclusive::new(*start, *end)); start = range.start(); end = range.end(); } if range.end() > end { end = range.end(); } } merged.push(RangeInclusive::new(*start, *end)); merged } impl FromStr for Ingredients { type Err = Report; fn from_str(s: &str) -> Result<Self, Self::Err> { let Some((fresh_str, avail_str)) = s.split_once("\n\n") else { bail!("missing divider"); }; let fresh = fresh_str .lines() .map(|l| -> Result<RangeInclusive<_>, Self::Err> { let (start, end) = l.split_once('-').ok_or_eyre("bad range")?; let start: Id = start.parse()?; let end: Id = end.parse()?; Ok(start..=end) }) .collect::<Result<_, _>>()?; let available = avail_str .lines() .map(|l| l.parse()) .collect::<Result<_, _>>()?; Ok(Ingredients { fresh: merge_ranges(fresh), available, }) } }Full code
use std::{collections::HashSet, fs, ops::RangeInclusive, str::FromStr}; use color_eyre::eyre::{OptionExt, Report, Result, bail}; #[derive(Debug, PartialEq, Eq)] struct Ingredients { fresh: Vec<RangeInclusive<Id>>, available: HashSet<Id>, } type Id = usize; impl Ingredients { fn is_fresh(&self, id: Id) -> bool { self.fresh.iter().any(|range| range.contains(&id)) } fn num_fresh_available(&self) -> usize { self.available .iter() .filter(|&&id| self.is_fresh(id)) .count() } fn num_fresh_all(&self) -> usize { self.fresh .iter() .map(|range| 1 + range.end() - range.start()) .sum() } } fn merge_ranges(mut ranges: Vec<RangeInclusive<Id>>) -> Vec<RangeInclusive<Id>> { if ranges.is_empty() { return ranges; } ranges.sort_by_key(|range| *range.start()); let mut merged = Vec::new(); let mut start = ranges[0].start(); let mut end = ranges[0].end(); for range in ranges.iter().skip(1) { if range.start() > end { merged.push(RangeInclusive::new(*start, *end)); start = range.start(); end = range.end(); } if range.end() > end { end = range.end(); } } merged.push(RangeInclusive::new(*start, *end)); merged } impl FromStr for Ingredients { type Err = Report; fn from_str(s: &str) -> Result<Self, Self::Err> { let Some((fresh_str, avail_str)) = s.split_once("\n\n") else { bail!("missing divider"); }; let fresh = fresh_str .lines() .map(|l| -> Result<RangeInclusive<_>, Self::Err> { let (start, end) = l.split_once('-').ok_or_eyre("bad range")?; let start: Id = start.parse()?; let end: Id = end.parse()?; Ok(start..=end) }) .collect::<Result<_, _>>()?; let available = avail_str .lines() .map(|l| l.parse()) .collect::<Result<_, _>>()?; Ok(Ingredients { fresh: merge_ranges(fresh), available, }) } } fn part1(filepath: &str) -> Result<usize> { let input = fs::read_to_string(filepath)?; let ingrd = Ingredients::from_str(&input).unwrap(); Ok(ingrd.num_fresh_available()) } fn part2(filepath: &str) -> Result<usize> { let input = fs::read_to_string(filepath)?; let ingrd = Ingredients::from_str(&input).unwrap(); Ok(ingrd.num_fresh_all()) } fn main() -> Result<()> { color_eyre::install()?; println!("Part 1: {}", part1("d05/input.txt")?); println!("Part 2: {}", part2("d05/input.txt")?); Ok(()) }