Skip to content

Commit 0eb7bd8

Browse files
committed
Day 11
1 parent 9063166 commit 0eb7bd8

File tree

2 files changed

+139
-1
lines changed

2 files changed

+139
-1
lines changed

src/day11.rs

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
use std::{collections::HashMap, fs, hash::Hash, ops::AddAssign};
2+
3+
pub fn day11(input_path: String) {
4+
let content = fs::read_to_string(input_path).unwrap();
5+
6+
let stones: Vec<_> = content.split_whitespace().map(Stone::parse).collect();
7+
8+
println!("{:?}", part1(stones.iter().cloned().collect()));
9+
println!("{:?}", part2(stones));
10+
}
11+
12+
// Most significant digit is last,
13+
// leading zeros are trimmed <- interesting: how do we enforce that?
14+
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
15+
struct Stone(Vec<u8>);
16+
17+
fn part1(stones: Vec<Stone>) -> usize {
18+
std::iter::successors(Some(stones), |stones| {
19+
Some(stones.iter().flat_map(|s| blink_at(s.clone())).collect())
20+
})
21+
.take(26)
22+
.last()
23+
.unwrap()
24+
.len()
25+
}
26+
27+
fn part2(stones: Vec<Stone>) -> usize {
28+
let mut stone_map: HashMap<Stone, usize> =
29+
accumulate_into_hashmap(stones.into_iter().map(|s| (s, 1)));
30+
31+
for _ in 0..75 {
32+
stone_map = accumulate_into_hashmap(
33+
stone_map
34+
.into_iter()
35+
.flat_map(|(stone, count)| blink_at(stone).into_iter().map(move |s| (s, count))),
36+
);
37+
}
38+
stone_map.iter().map(|(_, count)| count).sum()
39+
}
40+
41+
fn accumulate_into_hashmap<T, N, I>(values: I) -> HashMap<T, N>
42+
where
43+
T: Eq + Hash,
44+
N: Default + AddAssign,
45+
I: IntoIterator<Item = (T, N)>,
46+
{
47+
values
48+
.into_iter()
49+
.fold(HashMap::new(), |mut acc, (key, value)| {
50+
*acc.entry(key).or_default() += value;
51+
acc
52+
})
53+
}
54+
55+
fn blink_at(stone: Stone) -> Vec<Stone> {
56+
match stone {
57+
zero if zero.0.is_empty() => vec![Stone::from([1])],
58+
even if even.0.len() % 2 == 0 => Vec::from(even.split()),
59+
other => vec![other.muled_by(2024)],
60+
}
61+
}
62+
63+
impl Stone {
64+
// should actually be proper parse
65+
fn parse(string: &str) -> Stone {
66+
Stone::new(
67+
string
68+
.chars()
69+
.map(|c| c.to_digit(10).unwrap() as u8)
70+
.rev()
71+
.collect(),
72+
)
73+
}
74+
75+
fn new(digits: Vec<u8>) -> Stone {
76+
Stone(digits).trimmed()
77+
}
78+
79+
fn from<const N: usize>(arr: [u8; N]) -> Self {
80+
Stone::new(Vec::from(arr))
81+
}
82+
83+
fn split(mut self) -> [Stone; 2] {
84+
let second = self.0.split_off(self.0.len() / 2);
85+
[Stone::new(second), self.trimmed()]
86+
}
87+
88+
fn trimmed(mut self) -> Self {
89+
while self.0.last() == Some(&0) {
90+
self.0.pop();
91+
}
92+
self
93+
}
94+
95+
fn muled_by(mut self, n: usize) -> Self {
96+
let mut carry = 0usize;
97+
for d in self.0.iter_mut() {
98+
let value = (*d as usize) * n + carry;
99+
*d = (value % 10) as u8;
100+
carry = value / 10;
101+
}
102+
103+
while carry != 0 {
104+
self.0.push((carry % 10) as u8);
105+
carry /= 10;
106+
}
107+
self
108+
}
109+
}
110+
111+
#[cfg(test)]
112+
mod tests {
113+
use super::*;
114+
115+
#[test]
116+
fn test_muled_by_zero() {
117+
let stone = Stone::parse("123");
118+
let result = stone.muled_by(0);
119+
assert_eq!(result.0, vec![0, 0, 0]); // 123 * 0 = 0
120+
}
121+
122+
#[test]
123+
fn test_muled_by_one() {
124+
let stone = Stone::parse("456");
125+
let result = stone.muled_by(1);
126+
assert_eq!(result.0, vec![6, 5, 4]); // 456 * 1 = 456
127+
}
128+
129+
#[test]
130+
fn test_muled_by_large_number() {
131+
let stone = Stone::parse("999");
132+
let result = stone.muled_by(999);
133+
// 999 * 999 = 998001
134+
assert_eq!(result.0, vec![1, 0, 0, 8, 9, 9]);
135+
}
136+
}

src/main.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ mod day07;
1212
mod day08;
1313
mod day09;
1414
mod day10;
15+
mod day11;
1516

1617
#[derive(Parser)]
1718
struct Args {
@@ -34,6 +35,7 @@ fn main() {
3435
7 => day07::day07(args.input_path),
3536
8 => day08::day08(args.input_path),
3637
9 => day09::day09(args.input_path),
37-
_ => day10::day10(args.input_path),
38+
10 => day10::day10(args.input_path),
39+
_ => day11::day11(args.input_path),
3840
}
3941
}

0 commit comments

Comments
 (0)