diff --git a/utils/src/lib.rs b/utils/src/lib.rs index 22b5215..d8fe659 100644 --- a/utils/src/lib.rs +++ b/utils/src/lib.rs @@ -4,6 +4,7 @@ pub mod geom; pub mod grid; pub mod line; pub mod misc; +pub mod num; pub mod pos; pub mod range; @@ -16,6 +17,7 @@ pub mod prelude { pub use crate::grid::*; pub use crate::line::*; pub use crate::misc::*; + pub use crate::num::*; pub use crate::pos::*; pub use crate::range::*; } diff --git a/utils/src/num.rs b/utils/src/num.rs new file mode 100644 index 0000000..8d126c6 --- /dev/null +++ b/utils/src/num.rs @@ -0,0 +1,66 @@ +/// Gets the number of digits in a given number mathematically +/// +/// # Examples +/// +/// ``` +/// use utils::prelude::*; +/// +/// let my_num = 2226; +/// assert_eq!(num_digits(my_num), 4); +/// ``` +/// +/// ``` +/// use utils::prelude::*; +/// +/// let my_num = 120938747; +/// assert_eq!(num_digits(my_num), 9); +/// ``` +/// +/// ``` +/// use utils::prelude::*; +/// +/// assert_eq!(num_digits(0), 1); +/// ``` +/// +pub fn num_digits(num: usize) -> usize { + num.checked_ilog10().map(|x| x as usize).unwrap_or(0) + 1 +} + +/// Split a given number at a specific digit, this digit will be included in the right-hand side +/// and excluded in the left. +/// +/// If the split is invalid, zero may be returnd on either side of the result. +/// +/// # Examples +/// +/// ``` +/// use utils::prelude::*; +/// +/// let my_num = 123456; +/// let (left, right) = split_num_at(my_num, 3); +/// assert_eq!(left, 123); +/// assert_eq!(right, 456); +/// ``` +/// +pub fn split_num_at(num: usize, idx: u32) -> (usize, usize) { + let div = 10_usize.pow(idx); + (num / div, num % div) +} + +/// Split the given number once in the middle, see [[split_num_at]] for caveats. +/// +/// # Examples +/// +/// ``` +/// use utils::prelude::*; +/// +/// let my_num = 55556666; +/// let (left, right) = split_num_once(my_num); +/// assert_eq!(left, 5555); +/// assert_eq!(right, 6666); +/// ``` +/// +pub fn split_num_once(num: usize) -> (usize, usize) { + let digits = num_digits(num); + split_num_at(num, (digits / 2) as u32) +} diff --git a/years/2024/src/day_11.rs b/years/2024/src/day_11.rs index b2c3449..9c918bb 100644 --- a/years/2024/src/day_11.rs +++ b/years/2024/src/day_11.rs @@ -1,111 +1,58 @@ use std::collections::HashMap; use advent_core::{day_stuff, ex_for_day, Day}; +use utils::num::{num_digits, split_num_once}; pub struct Day11; -impl Day for Day11 { - day_stuff!(11, "55312", "65601038650482", Vec); - - fn part_1(input: Self::Input) -> Option { - let l = input.len(); - let mut stone_map = input - .into_iter() - .fold(HashMap::with_capacity(l), |mut acc, stone| { - acc.entry(stone).and_modify(|c| *c += 1).or_insert(1); - acc - }); - - for _i in 0..25 { - let mut new_map = HashMap::with_capacity(stone_map.len()); - - for (num, count) in stone_map { - if num == 0 { - new_map - .entry(1) - .and_modify(|c| *c += count) - .or_insert(count); +fn do_blinks(stones: Vec, blinks: usize) -> usize { + let l = stones.len(); + let mut stone_map = stones + .into_iter() + .fold(HashMap::with_capacity(l), |mut acc, stone| { + acc.entry(stone).and_modify(|c| *c += 1).or_insert(1); + acc + }); + + for _i in 0..blinks { + let mut new_map = HashMap::with_capacity(stone_map.len() * 2); + + for (num, count) in stone_map { + if num == 0 { + *new_map.entry(1).or_insert(0) += count; + } else { + if num_digits(num) % 2 == 0 { + let (left, right) = split_num_once(num); + *new_map.entry(left).or_insert(0) += count; + *new_map.entry(right).or_insert(0) += count; } else { - let ss = num.to_string(); - let l = ss.len(); - if l % 2 == 0 { - let (num1, num2) = - (ss[..l / 2].parse().unwrap(), ss[l / 2..].parse().unwrap()); - new_map - .entry(num1) - .and_modify(|c| *c += count) - .or_insert(count); - new_map - .entry(num2) - .and_modify(|c| *c += count) - .or_insert(count); - } else { - new_map - .entry(num * 2024) - .and_modify(|c| *c += count) - .or_insert(count); - } + *new_map.entry(num * 2024).or_insert(0) += count; } } - - stone_map = new_map; } - Some(stone_map.values().sum::().to_string()) + stone_map = new_map; } - fn part_2(input: Self::Input) -> Option { - let l = input.len(); - let mut stone_map = input - .into_iter() - .fold(HashMap::with_capacity(l), |mut acc, stone| { - acc.entry(stone).and_modify(|c| *c += 1).or_insert(1); - acc - }); - - for _i in 0..75 { - let mut new_map = HashMap::with_capacity(stone_map.len()); + stone_map.into_values().sum::() +} - for (num, count) in stone_map { - if num == 0 { - new_map - .entry(1) - .and_modify(|c| *c += count) - .or_insert(count); - } else { - let ss = num.to_string(); - let l = ss.len(); - if l % 2 == 0 { - let (num1, num2) = - (ss[..l / 2].parse().unwrap(), ss[l / 2..].parse().unwrap()); - new_map - .entry(num1) - .and_modify(|c| *c += count) - .or_insert(count); - new_map - .entry(num2) - .and_modify(|c| *c += count) - .or_insert(count); - } else { - new_map - .entry(num * 2024) - .and_modify(|c| *c += count) - .or_insert(count); - } - } - } +impl Day for Day11 { + day_stuff!(11, "55312", "65601038650482", Vec); - stone_map = new_map; - } + fn part_1(input: Self::Input) -> Option { + Some(do_blinks(input, 25).to_string()) + } - Some(stone_map.values().sum::().to_string()) + fn part_2(input: Self::Input) -> Option { + Some(do_blinks(input, 75).to_string()) } fn parse_input(input: &str) -> Self::Input { input .trim() .split(" ") - .map(|n| n.parse::().unwrap()) + .map(|n| n.parse::().unwrap()) .collect() } }