adventofcode/days/2024/11.rs

197 Zeilen
5,6 KiB
Rust

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<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() {
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<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);
}
}