Skip to content

Commit 3b31b4c

Browse files
committed
Work a bit on Day 11
1 parent 0eb7bd8 commit 3b31b4c

File tree

1 file changed

+201
-35
lines changed

1 file changed

+201
-35
lines changed

src/day11.rs

Lines changed: 201 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1-
use std::{collections::HashMap, fs, hash::Hash, ops::AddAssign};
1+
use std::{collections::HashMap, fs, hash::Hash, num::ParseIntError, ops::AddAssign, str::FromStr};
22

33
pub fn day11(input_path: String) {
44
let content = fs::read_to_string(input_path).unwrap();
55

6-
let stones: Vec<_> = content.split_whitespace().map(Stone::parse).collect();
6+
let stones: Vec<_> = content
7+
.split_whitespace()
8+
.map(|l| l.parse().unwrap())
9+
.collect();
710

811
println!("{:?}", part1(stones.iter().cloned().collect()));
9-
println!("{:?}", part2(stones));
12+
println!("{:?}", part2(&stones));
13+
14+
15+
let stones: Vec<_> = content
16+
.split_whitespace()
17+
.map(|l| l.parse().unwrap()) // this deduces Stone2 because of the signature of part12
18+
.collect();
19+
20+
println!("{:?}", part12(&stones));
21+
println!("{:?}", part22(&stones));
1022
}
1123

1224
// Most significant digit is last,
@@ -24,12 +36,12 @@ fn part1(stones: Vec<Stone>) -> usize {
2436
.len()
2537
}
2638

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)));
39+
fn part2(stones: &[Stone]) -> usize {
40+
// order does not matter, so we can compress the stones into a hashmap, and do every operation just once
41+
let mut stone_map: HashMap<Stone, usize> = accumulate(stones.iter().map(|s| (s.clone(), 1)));
3042

3143
for _ in 0..75 {
32-
stone_map = accumulate_into_hashmap(
44+
stone_map = accumulate(
3345
stone_map
3446
.into_iter()
3547
.flat_map(|(stone, count)| blink_at(stone).into_iter().map(move |s| (s, count))),
@@ -38,20 +50,6 @@ fn part2(stones: Vec<Stone>) -> usize {
3850
stone_map.iter().map(|(_, count)| count).sum()
3951
}
4052

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-
5553
fn blink_at(stone: Stone) -> Vec<Stone> {
5654
match stone {
5755
zero if zero.0.is_empty() => vec![Stone::from([1])],
@@ -61,17 +59,6 @@ fn blink_at(stone: Stone) -> Vec<Stone> {
6159
}
6260

6361
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-
7562
fn new(digits: Vec<u8>) -> Stone {
7663
Stone(digits).trimmed()
7764
}
@@ -108,29 +95,208 @@ impl Stone {
10895
}
10996
}
11097

98+
#[derive(Debug, PartialEq, Eq)]
99+
struct ParseStoneError;
100+
101+
impl FromStr for Stone {
102+
type Err = ParseStoneError;
103+
104+
// this is just for illustrating
105+
// fn from_str(s: &str) -> Result<Self, Self::Err> {
106+
// let digits: Result<Vec<u8>, ParseStoneError> = s
107+
// .chars()
108+
// .map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
109+
// .rev()
110+
// // this collect flips and creates a vector,
111+
// // i.e. it does
112+
// // Iter<Result<u8, Error>> -> Result<Vec<u8>, Error>
113+
// // i.e. it does "early return with Result"
114+
// .collect();
115+
// Ok(Stone::new(digits?))
116+
// }
117+
118+
fn from_str(s: &str) -> Result<Self, Self::Err> {
119+
s.chars()
120+
.map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
121+
.rev()
122+
.collect::<Result<_, _>>()
123+
.map(Stone::new)
124+
}
125+
}
126+
127+
fn accumulate<T, N, I>(values: I) -> HashMap<T, N>
128+
where
129+
T: Eq + Hash,
130+
N: Default + AddAssign,
131+
I: IntoIterator<Item = (T, N)>,
132+
{
133+
values
134+
.into_iter()
135+
.fold(HashMap::new(), |mut acc, (key, value)| {
136+
*acc.entry(key).or_default() += value;
137+
acc
138+
})
139+
}
140+
141+
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
142+
struct Stone2 {
143+
number: usize,
144+
digits: u32,
145+
}
146+
147+
fn digits(number: usize) -> u32 {
148+
if number == 0 {
149+
return 1;
150+
}
151+
let mut temp = number;
152+
let mut digit_count = 0;
153+
while temp > 0 {
154+
temp /= 10;
155+
digit_count += 1;
156+
}
157+
digit_count
158+
}
159+
160+
impl Stone2 {
161+
fn new(number: usize) -> Self {
162+
Stone2 {
163+
number,
164+
digits: digits(number),
165+
}
166+
}
167+
168+
fn split(self) -> [Self; 2] {
169+
// calling this with odd number of digits is ub (I'm lazy)
170+
let divisor = 10usize.pow(self.digits / 2);
171+
let left = Stone2 {
172+
number: self.number / divisor,
173+
digits: self.digits / 2,
174+
};
175+
// right could have leading zeros, recompute digits
176+
let right = Stone2::new(self.number % divisor);
177+
[left, right]
178+
}
179+
180+
fn muled_by(self, n: usize) -> Self {
181+
Stone2::new(self.number * n)
182+
}
183+
}
184+
185+
// here, returning impl Iterator<Item = Stone2>
186+
// does not work, because all match arms would need to have the same concrete type
187+
// (which they cannot have, except when we'd implement a bespoke OneOrTwo-struct)
188+
fn blink_at2(stone: Stone2) -> Vec<Stone2> {
189+
match stone {
190+
Stone2 {
191+
number: 0,
192+
digits: 1,
193+
} => vec![Stone2 {
194+
number: 1,
195+
digits: 1,
196+
}],
197+
even if even.digits % 2 == 0 => Vec::from(even.split()),
198+
other => vec![other.muled_by(2024)],
199+
}
200+
}
201+
202+
fn do_rounds(stones: &[Stone2], rounds: usize) -> usize {
203+
let mut stone_map = accumulate(stones.iter().map(|s| (s.clone(), 1)));
204+
205+
for _ in 0..rounds {
206+
stone_map = accumulate(
207+
stone_map
208+
.into_iter()
209+
.flat_map(|(stone, count)| blink_at2(stone).into_iter().map(move |s| (s, count))),
210+
);
211+
}
212+
stone_map.iter().map(|(_, count)| count).sum()
213+
}
214+
215+
fn part12(stones: &[Stone2]) -> usize {
216+
do_rounds(stones, 25)
217+
}
218+
219+
fn part22(stones: &[Stone2]) -> usize {
220+
do_rounds(stones, 75)
221+
}
222+
223+
impl FromStr for Stone2 {
224+
type Err = ParseIntError;
225+
226+
fn from_str(s: &str) -> Result<Self, Self::Err> {
227+
s.parse().map(|ds| Stone2::new(ds))
228+
}
229+
}
230+
111231
#[cfg(test)]
112232
mod tests {
113233
use super::*;
114234

115235
#[test]
116236
fn test_muled_by_zero() {
117-
let stone = Stone::parse("123");
237+
let stone: Stone = "123".parse().unwrap();
118238
let result = stone.muled_by(0);
119239
assert_eq!(result.0, vec![0, 0, 0]); // 123 * 0 = 0
120240
}
121241

122242
#[test]
123243
fn test_muled_by_one() {
124-
let stone = Stone::parse("456");
244+
let stone: Stone = "456".parse().unwrap();
125245
let result = stone.muled_by(1);
126246
assert_eq!(result.0, vec![6, 5, 4]); // 456 * 1 = 456
127247
}
128248

129249
#[test]
130250
fn test_muled_by_large_number() {
131-
let stone = Stone::parse("999");
251+
let stone: Stone = "999".parse().unwrap();
132252
let result = stone.muled_by(999);
133253
// 999 * 999 = 998001
134254
assert_eq!(result.0, vec![1, 0, 0, 8, 9, 9]);
135255
}
256+
257+
#[test]
258+
fn test_muled_by_zero2() {
259+
let stone: Stone2 = "123".parse().unwrap();
260+
let result = stone.muled_by(0);
261+
assert_eq!(
262+
result,
263+
Stone2 {
264+
number: 0,
265+
digits: 1
266+
}
267+
); // 123 * 0 = 0
268+
}
269+
270+
#[test]
271+
fn test_muled_by_one2() {
272+
let stone: Stone2 = "456".parse().unwrap();
273+
let result = stone.muled_by(1);
274+
assert_eq!(
275+
result,
276+
Stone2 {
277+
number: 456,
278+
digits: 3
279+
}
280+
); // 456 * 1 = 456
281+
}
282+
283+
#[test]
284+
fn test_muled_by_large_number2() {
285+
let stone: Stone2 = "999".parse().unwrap();
286+
let result = stone.muled_by(999);
287+
assert_eq!(
288+
result,
289+
Stone2 {
290+
number: 998001,
291+
digits: 6
292+
}
293+
); // 999 * 999 = 998001
294+
}
295+
296+
#[test]
297+
fn test_split() {
298+
let stone: Stone2 = "1001".parse().unwrap();
299+
let result = stone.split();
300+
assert_eq!(result, [Stone2::new(10), Stone2::new(1)]);
301+
}
136302
}

0 commit comments

Comments
 (0)