2024-12-14 11:48:20 +01:00
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
|
|
use advent_of_code::{
|
|
|
|
numberlength,
|
|
|
|
strings::{convert_to_array, parsenumber},
|
|
|
|
ExtendedOption,
|
|
|
|
};
|
2024-12-19 17:12:39 +01:00
|
|
|
use advent_of_code_macros::include_aoc;
|
2024-12-14 11:48:20 +01:00
|
|
|
use num::Integer;
|
|
|
|
|
2024-12-19 17:12:39 +01:00
|
|
|
include_aoc!(2024, 11);
|
2024-12-14 11:48:20 +01:00
|
|
|
|
|
|
|
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<Stone>,
|
|
|
|
map: HashMap<u64, ExtendedOption<MapSkip>>,
|
|
|
|
stones: u64,
|
|
|
|
start: usize,
|
|
|
|
stones_billions: u64,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl StoneCounter {
|
|
|
|
fn new(start_values: Vec<u64>, rounds: usize) -> StoneCounter {
|
|
|
|
let mut cache = Vec::with_capacity(start_values.len());
|
|
|
|
for v in start_values.iter() {
|
2024-12-16 22:12:30 +01:00
|
|
|
cache.push(Stone {
|
|
|
|
number: *v,
|
|
|
|
left: rounds,
|
|
|
|
});
|
2024-12-14 11:48:20 +01:00
|
|
|
}
|
|
|
|
StoneCounter {
|
2024-12-16 22:12:30 +01:00
|
|
|
cache,
|
2024-12-14 11:48:20 +01:00
|
|
|
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;
|
2024-12-16 22:12:30 +01:00
|
|
|
self.cache.push(Stone {
|
|
|
|
number: skip.newnumber_b,
|
|
|
|
left: stone.left,
|
|
|
|
});
|
2024-12-14 11:48:20 +01:00
|
|
|
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<u64>) {
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|