diff --git a/day11/src/main.rs b/day11/src/main.rs index fd87547..caca1b3 100644 --- a/day11/src/main.rs +++ b/day11/src/main.rs @@ -1,4 +1,4 @@ -use std::collections::VecDeque; +use std::collections::HashMap; fn main() { let input = std::fs::read_to_string("input").unwrap(); @@ -7,142 +7,68 @@ fn main() { println!("{}", pt2(&stones)); } -fn pt1(stones: &[u128]) -> usize { - blink2(stones, 25) +fn pt1(stones: &[u64]) -> usize { + b4(stones, 25) } -fn pt2(stones: &[u128]) -> usize { - let mut total = 0; - for stone in stones { - total += b4(vec![*stone], 75); +fn pt2(stones: &[u64]) -> usize { + b4(stones, 75) +} + +fn b4(stones: &[u64], blinks: usize) -> usize { + let mut cache = HashMap::new(); + for &stone in stones { + cache + .entry(stone) + .and_modify(|n| *n += 1usize) + .or_insert(1usize); } - total -} - -fn blink2(stones: &[u128], blinks: usize) -> usize { - let mut stones = VecDeque::from_iter(stones.iter().copied()); for _blink in 0..blinks { - let mut rots = 0; - while rots < stones.len() { - let stone = stones[0]; - let digits = stone.to_string(); - + let mut new_cache = HashMap::new(); + for (stone, count) in cache.into_iter() { match stone { 0 => { - stones[0] = 1; - stones.rotate_left(1); - rots += 1; + new_cache + .entry(1) + .and_modify(|n| *n += count) + .or_insert(count); } - _ if digits.len() % 2 == 0 => { - let mid = digits.len() / 2; - let left = digits[0..mid].parse().unwrap(); - let right = digits[mid..].parse().unwrap(); - stones[0] = left; - stones.rotate_left(1); - stones.push_front(right); - stones.rotate_left(1); - rots += 2; + _ if (stone.checked_ilog10().unwrap_or(0) + 1) % 2 == 0 => { + let (left, right) = split(stone); + new_cache + .entry(left) + .and_modify(|n| *n += count) + .or_insert(count); + new_cache + .entry(right) + .and_modify(|n| *n += count) + .or_insert(count); } _ => { - stones[0] = stone * 2024; - stones.rotate_left(1); - rots += 1; + let new = stone * 2024; + new_cache + .entry(new) + .and_modify(|n| *n += count) + .or_insert(count); } } } - stones.rotate_right(rots); - } - stones.len() -} - -fn b4(stones: Vec<u128>, blinks: usize) -> usize { - let mut total = stones.len(); - let mut stones = VecDeque::from_iter(stones); - - for blink in 0..blinks { - let len = stones.len(); - if len > 10_000_000 { - println!("splitting; started with {blinks} blinks to go, blinked {blink} times"); - let mid = len / 2; - let stones = stones.make_contiguous().to_vec(); - let iter = &mut stones.into_iter(); - let left: Vec<_> = iter.take(mid).collect(); - let right: Vec<_> = iter.collect(); - // otherwise it gets double-counted at the start of the function in the - // recursive call - total -= left.len(); - total -= right.len(); - let blinks = blinks - blink; - total += b4(left, blinks); - total += b4(right, blinks); - break; - } else { - let mut rots = 0; - while rots < stones.len() { - let stone = stones[0]; - let digits = stone.to_string(); - - match stone { - 0 => { - stones[0] = 1; - stones.rotate_left(1); - rots += 1; - } - _ if digits.len() % 2 == 0 => { - let mid = digits.len() / 2; - let left = digits[0..mid].parse().unwrap(); - let right = digits[mid..].parse().unwrap(); - stones[0] = left; - stones.rotate_left(1); - stones.push_front(right); - stones.rotate_left(1); - rots += 2; - total += 1; - } - _ => { - stones[0] = stone * 2024; - stones.rotate_left(1); - rots += 1; - } - } - } - stones.rotate_right(rots); - } - } - total -} - -fn blink3(mut stones: Vec<u128>, blinks: usize) -> usize { - let mut total = stones.len(); - for blink in 0..blinks { - let mut rights = Vec::new(); - for stone in stones.iter_mut() { - let digits = stone.to_string(); - match stone { - 0 => { - *stone = 1; - } - _ if digits.len() % 2 == 0 => { - let mid = digits.len() / 2; - let left = digits[0..mid].parse().unwrap(); - let right = digits[mid..].parse().unwrap(); - *stone = left; - rights.push(right); - total += 1; - } - _ => { - *stone *= 2024; - } - } - } - total += blink3(rights, blinks - blink); + cache = new_cache; } - total + cache.values().sum() } -fn parse(input: &str) -> Vec<u128> { +fn split(num: u64) -> (u64, u64) { + let num_digits = num.ilog10() + 1; + let div = num_digits / 2; + let left = num.div_euclid(10u64.pow(div)); + let right = num.rem_euclid(10u64.pow(div)); + (left, right) +} + +fn parse(input: &str) -> Vec<u64> { input .trim() .split(' ') @@ -159,10 +85,4 @@ mod test { let stones = parse("125 17"); assert_eq!(55312, pt1(&stones)); } - - #[test] - fn p2() { - let stones = parse("125 17"); - assert_eq!(55312, b4(stones, 25)); - } }