Day 2: Gift Shop
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


Another day where the dumb way would have so much quicker and easier, but I’m not competing for time.
I decided to solve it numerically without regex or using
to_string(), which was more taxing for the ol’ grey matter but is perhaps fairly optimal (if I bothered to pre-compute all those pow() calls, anyway).Part 2 runs in 35ms (on my AMD Ryzen 7 9800X3D), whereas the to_string() version runs in 40ms. So… not really worth it, and it’s less readable.
Rust
use std::fs; use color_eyre::eyre::{Result, bail}; type InvalidChecker = fn(usize) -> bool; fn sum_invalids(input: &str, checkfn: InvalidChecker) -> Result<usize> { let total = input .trim() .split(',') .map(|idrange| { if let Some((start, end)) = idrange.split_once('-') { let mut sum = 0; for n in start.parse::<usize>()?..=end.parse::<usize>()? { if checkfn(n) { sum += n; } } Ok(sum) } else { bail!("Couldn't parse {idrange}") } }) .sum::<Result<usize, _>>()?; Ok(total) } fn is_invalid_p1(n: usize) -> bool { let len = n.ilog10() + 1; // odd-length numbers can't repeat if len % 2 == 1 { return false; } let lhs = n / 10_usize.pow(len / 2); let rhs = n - (lhs * 10_usize.pow(len / 2)); lhs == rhs } const SPANS: &[&[u32]] = &[ &[], // i = 0 &[], // i = 1 &[1], // i = 2 &[1], // i = 3 &[1, 2], // i = 4 &[1], // i = 5 &[1, 2, 3], // i = 6 &[1], // i = 7 &[1, 2, 4], // i = 8 &[1, 3], // i = 9 &[1, 2, 5], // i = 10 &[1], // i = 11 &[1, 2, 3, 4, 6], // i = 12 ]; fn is_invalid_p2(n: usize) -> bool { let len = n.ilog10() + 1; // 1-length numbers can't repeat if len == 1 { return false; } SPANS[len as usize].iter().any(|&span| { let lhs = n / 10_usize.pow(len - span); let mut remainder = n; let mut rhs = lhs; (2..=(len / span)).all(|i| { remainder -= rhs * 10_usize.pow(len - (i - 1) * span); rhs = remainder / 10_usize.pow(len - i * span); lhs == rhs }) }) } fn part1(filepath: &str) -> Result<usize> { let input = fs::read_to_string(filepath)?; let res = sum_invalids(&input, is_invalid_p1)?; Ok(res) } fn part2(filepath: &str) -> Result<usize> { let input = fs::read_to_string(filepath)?; let res = sum_invalids(&input, is_invalid_p2)?; Ok(res) }to_string version:
fn is_invalid_p2(n: usize) -> bool { let s = n.to_string(); let len = s.len(); // 1-length numbers can't repeat if len == 1 { return false; } SPANS[len].iter().any(|&span| { let span = span as usize; let lhs = &s[0..span].as_bytes(); s.as_bytes().chunks(span).all(|rhs| *lhs == rhs) }) }