use std::collections::HashMap; use advent_of_code::{ numberlength, strings::{convert_to_array, parsenumber}, ExtendedOption, }; use advent_of_code_macros::include_aoc; use num::Integer; include_aoc!(2024, 11); const BILLIONS: u64 = 1000_000_000; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct Stone { number: u64, left: usize, } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] struct MapSkip { newnumber_a: u64, newnumber_b: u64, skipped_steps: usize, } #[derive(Debug, Clone)] struct StoneCounter { cache: Vec, map: HashMap>, stones: u64, start: usize, stones_billions: u64, } impl StoneCounter { fn new(start_values: Vec, rounds: usize) -> StoneCounter { let mut cache = Vec::with_capacity(start_values.len()); for v in start_values.iter() { cache.push(Stone { number: *v, left: rounds, }); } StoneCounter { cache, map: HashMap::with_capacity(10_000), stones: 0, start: rounds, stones_billions: 0, } } fn blink(&mut self) -> u64 { let mut stone: Stone; let mut copy: Stone; let (mut next, mut second); while self.cache.len() > 0 { stone = self.cache.pop().unwrap(); copy = stone.clone(); while stone.left > 0 { if self.map.contains_key(&stone.number) { match self.map.get(&stone.number).unwrap() { ExtendedOption::None => { self.stones += 1; break; }, ExtendedOption::Some(skip) => { if stone.left < skip.skipped_steps { stone.left = 0 } else { stone.left -= skip.skipped_steps; } stone.number = skip.newnumber_a; self.cache.push(Stone { number: skip.newnumber_b, left: stone.left, }); continue; }, ExtendedOption::Unset => {}, } } if stone.left == 1 { self.map = self .map .iter() .filter_map(|x| { Some(if *x.1 == ExtendedOption::Unset { (*x.0, ExtendedOption::None) } else { (*x.0, *x.1) }) }) .collect(); break; } else { self.map.insert(stone.number, ExtendedOption::Unset); } stone.left -= 1; (next, second) = blink(stone.number); match second { None => { self.map.insert(stone.number, ExtendedOption::Unset); stone.number = next; }, Some(sec) => { self.map.insert( stone.number, ExtendedOption::Some(MapSkip { newnumber_a: next, newnumber_b: sec, skipped_steps: copy.left - stone.left, }), ); }, } } } self.stones_billions * BILLIONS + self.stones } } fn combine_number(p: &[u64]) -> u64 { let mut o = 0; for i in p { o = o * 10 + i; } o } fn splitnumber(i: u64) -> (u64, u64) { let len = numberlength(i) as usize; let mut array = Vec::with_capacity(len); let (mut quot, mut remainder); quot = i; loop { (quot, remainder) = quot.div_rem(&10); array.insert(0, remainder); if quot == 0 { break; } } let (a, b) = array.split_at(len / 2); (combine_number(a), combine_number(b)) } fn blink(stone: u64) -> (u64, Option) { if stone == 0 { (1, None) } else if numberlength(stone) % 2 == 0 { let (stone, t) = splitnumber(stone); (stone, Some(t)) } else { (stone * 2024, None) } } fn main() { let stoneline = convert_to_array::<_, _, ' '>(DATA, parsenumber); let mut counter = StoneCounter::new(stoneline.clone(), 25); let stones = counter.blink(); debug_assert!(stones == 235850, "{} != {}", stones, 235850); println!("{:?} Stones after 25 blinks", stones); counter = StoneCounter::new(stoneline, 75); println!("{:?} Stones after 75 blinks", counter.blink()); } #[cfg(test)] mod test { use super::*; #[test] fn test_blink() { let mut res = blink(8); assert_eq!(res, (16192, None)); res = blink(res.0); assert_eq!(res, (32772608, None)); res = blink(res.0); assert_eq!(res, (3277, Some(2608))); } #[test] fn test_stones() { let testvalues = vec![ 125, 17, ]; let mut counter = StoneCounter::new(testvalues.clone(), 6); assert_eq!(counter.blink(), 22); counter = StoneCounter::new(testvalues.clone(), 25); assert_eq!(counter.blink(), 55312); } }