1
- use std:: { collections:: HashMap , fs, hash:: Hash , ops:: AddAssign } ;
1
+ use std:: { collections:: HashMap , fs, hash:: Hash , num :: ParseIntError , ops:: AddAssign , str :: FromStr } ;
2
2
3
3
pub fn day11 ( input_path : String ) {
4
4
let content = fs:: read_to_string ( input_path) . unwrap ( ) ;
5
5
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 ( ) ;
7
10
8
11
println ! ( "{:?}" , part1( stones. iter( ) . cloned( ) . collect( ) ) ) ;
9
- println ! ( "{:?}" , part2( stones) ) ;
12
+ println ! ( "{:?}" , part2( & stones) ) ;
13
+
14
+ let stones: Vec < _ > = content
15
+ . split_whitespace ( )
16
+ . map ( |l| l. parse ( ) . unwrap ( ) ) // this deduces Stone2 because of the signature of part12
17
+ . collect ( ) ;
18
+
19
+ println ! ( "{:?}" , part12( & stones) ) ;
20
+ println ! ( "{:?}" , part22( & stones) ) ;
10
21
}
11
22
12
23
// Most significant digit is last,
@@ -24,12 +35,12 @@ fn part1(stones: Vec<Stone>) -> usize {
24
35
. len ( )
25
36
}
26
37
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 ) ) ) ;
38
+ fn part2 ( stones : & [ Stone ] ) -> usize {
39
+ // order does not matter, so we can compress the stones into a hashmap, and do every operation just once
40
+ let mut stone_map : HashMap < Stone , usize > = accumulate ( stones. iter ( ) . map ( |s| ( s. clone ( ) , 1 ) ) ) ;
30
41
31
42
for _ in 0 ..75 {
32
- stone_map = accumulate_into_hashmap (
43
+ stone_map = accumulate (
33
44
stone_map
34
45
. into_iter ( )
35
46
. flat_map ( |( stone, count) | blink_at ( stone) . into_iter ( ) . map ( move |s| ( s, count) ) ) ,
@@ -38,20 +49,6 @@ fn part2(stones: Vec<Stone>) -> usize {
38
49
stone_map. iter ( ) . map ( |( _, count) | count) . sum ( )
39
50
}
40
51
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
52
fn blink_at ( stone : Stone ) -> Vec < Stone > {
56
53
match stone {
57
54
zero if zero. 0 . is_empty ( ) => vec ! [ Stone :: from( [ 1 ] ) ] ,
@@ -61,17 +58,6 @@ fn blink_at(stone: Stone) -> Vec<Stone> {
61
58
}
62
59
63
60
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
61
fn new ( digits : Vec < u8 > ) -> Stone {
76
62
Stone ( digits) . trimmed ( )
77
63
}
@@ -108,29 +94,208 @@ impl Stone {
108
94
}
109
95
}
110
96
97
+ #[ derive( Debug , PartialEq , Eq ) ]
98
+ struct ParseStoneError ;
99
+
100
+ impl FromStr for Stone {
101
+ type Err = ParseStoneError ;
102
+
103
+ // this is just for illustrating
104
+ // fn from_str(s: &str) -> Result<Self, Self::Err> {
105
+ // let digits: Result<Vec<u8>, ParseStoneError> = s
106
+ // .chars()
107
+ // .map(|c| c.to_digit(10).map(|d| d as u8).ok_or(ParseStoneError))
108
+ // .rev()
109
+ // // this collect flips and creates a vector,
110
+ // // i.e. it does
111
+ // // Iter<Result<u8, Error>> -> Result<Vec<u8>, Error>
112
+ // // i.e. it does "early return with Result"
113
+ // .collect();
114
+ // Ok(Stone::new(digits?))
115
+ // }
116
+
117
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
118
+ s. chars ( )
119
+ . map ( |c| c. to_digit ( 10 ) . map ( |d| d as u8 ) . ok_or ( ParseStoneError ) )
120
+ . rev ( )
121
+ . collect :: < Result < _ , _ > > ( )
122
+ . map ( Stone :: new)
123
+ }
124
+ }
125
+
126
+ fn accumulate < T , N , I > ( values : I ) -> HashMap < T , N >
127
+ where
128
+ T : Eq + Hash ,
129
+ N : Default + AddAssign ,
130
+ I : IntoIterator < Item = ( T , N ) > ,
131
+ {
132
+ values
133
+ . into_iter ( )
134
+ . fold ( HashMap :: new ( ) , |mut acc, ( key, value) | {
135
+ * acc. entry ( key) . or_default ( ) += value;
136
+ acc
137
+ } )
138
+ }
139
+
140
+ #[ derive( Debug , PartialEq , Eq , Clone , Hash ) ]
141
+ struct Stone2 {
142
+ number : usize ,
143
+ digits : u32 ,
144
+ }
145
+
146
+ fn digits ( number : usize ) -> u32 {
147
+ if number == 0 {
148
+ return 1 ;
149
+ }
150
+ let mut temp = number;
151
+ let mut digit_count = 0 ;
152
+ while temp > 0 {
153
+ temp /= 10 ;
154
+ digit_count += 1 ;
155
+ }
156
+ digit_count
157
+ }
158
+
159
+ impl Stone2 {
160
+ fn new ( number : usize ) -> Self {
161
+ Stone2 {
162
+ number,
163
+ digits : digits ( number) ,
164
+ }
165
+ }
166
+
167
+ fn split ( self ) -> [ Self ; 2 ] {
168
+ // calling this with odd number of digits is ub (I'm lazy)
169
+ let divisor = 10usize . pow ( self . digits / 2 ) ;
170
+ let left = Stone2 {
171
+ number : self . number / divisor,
172
+ digits : self . digits / 2 ,
173
+ } ;
174
+ // right could have leading zeros, recompute digits
175
+ let right = Stone2 :: new ( self . number % divisor) ;
176
+ [ left, right]
177
+ }
178
+
179
+ fn muled_by ( self , n : usize ) -> Self {
180
+ Stone2 :: new ( self . number * n)
181
+ }
182
+ }
183
+
184
+ // here, returning impl Iterator<Item = Stone2>
185
+ // does not work, because all match arms would need to have the same concrete type
186
+ // (which they cannot have, except when we'd implement a bespoke OneOrTwo-struct)
187
+ fn blink_at2 ( stone : Stone2 ) -> Vec < Stone2 > {
188
+ match stone {
189
+ Stone2 {
190
+ number : 0 ,
191
+ digits : 1 ,
192
+ } => vec ! [ Stone2 {
193
+ number: 1 ,
194
+ digits: 1 ,
195
+ } ] ,
196
+ even if even. digits % 2 == 0 => Vec :: from ( even. split ( ) ) ,
197
+ other => vec ! [ other. muled_by( 2024 ) ] ,
198
+ }
199
+ }
200
+
201
+ fn do_rounds ( stones : & [ Stone2 ] , rounds : usize ) -> usize {
202
+ let mut stone_map = accumulate ( stones. iter ( ) . map ( |s| ( s. clone ( ) , 1 ) ) ) ;
203
+
204
+ for _ in 0 ..rounds {
205
+ stone_map = accumulate (
206
+ stone_map
207
+ . into_iter ( )
208
+ . flat_map ( |( stone, count) | blink_at2 ( stone) . into_iter ( ) . map ( move |s| ( s, count) ) ) ,
209
+ ) ;
210
+ }
211
+ stone_map. iter ( ) . map ( |( _, count) | count) . sum ( )
212
+ }
213
+
214
+ fn part12 ( stones : & [ Stone2 ] ) -> usize {
215
+ do_rounds ( stones, 25 )
216
+ }
217
+
218
+ fn part22 ( stones : & [ Stone2 ] ) -> usize {
219
+ do_rounds ( stones, 75 )
220
+ }
221
+
222
+ impl FromStr for Stone2 {
223
+ type Err = ParseIntError ;
224
+
225
+ fn from_str ( s : & str ) -> Result < Self , Self :: Err > {
226
+ s. parse ( ) . map ( |ds| Stone2 :: new ( ds) )
227
+ }
228
+ }
229
+
111
230
#[ cfg( test) ]
112
231
mod tests {
113
232
use super :: * ;
114
233
115
234
#[ test]
116
235
fn test_muled_by_zero ( ) {
117
- let stone = Stone :: parse ( "123" ) ;
236
+ let stone: Stone = "123" . parse ( ) . unwrap ( ) ;
118
237
let result = stone. muled_by ( 0 ) ;
119
238
assert_eq ! ( result. 0 , vec![ 0 , 0 , 0 ] ) ; // 123 * 0 = 0
120
239
}
121
240
122
241
#[ test]
123
242
fn test_muled_by_one ( ) {
124
- let stone = Stone :: parse ( "456" ) ;
243
+ let stone: Stone = "456" . parse ( ) . unwrap ( ) ;
125
244
let result = stone. muled_by ( 1 ) ;
126
245
assert_eq ! ( result. 0 , vec![ 6 , 5 , 4 ] ) ; // 456 * 1 = 456
127
246
}
128
247
129
248
#[ test]
130
249
fn test_muled_by_large_number ( ) {
131
- let stone = Stone :: parse ( "999" ) ;
250
+ let stone: Stone = "999" . parse ( ) . unwrap ( ) ;
132
251
let result = stone. muled_by ( 999 ) ;
133
252
// 999 * 999 = 998001
134
253
assert_eq ! ( result. 0 , vec![ 1 , 0 , 0 , 8 , 9 , 9 ] ) ;
135
254
}
255
+
256
+ #[ test]
257
+ fn test_muled_by_zero2 ( ) {
258
+ let stone: Stone2 = "123" . parse ( ) . unwrap ( ) ;
259
+ let result = stone. muled_by ( 0 ) ;
260
+ assert_eq ! (
261
+ result,
262
+ Stone2 {
263
+ number: 0 ,
264
+ digits: 1
265
+ }
266
+ ) ; // 123 * 0 = 0
267
+ }
268
+
269
+ #[ test]
270
+ fn test_muled_by_one2 ( ) {
271
+ let stone: Stone2 = "456" . parse ( ) . unwrap ( ) ;
272
+ let result = stone. muled_by ( 1 ) ;
273
+ assert_eq ! (
274
+ result,
275
+ Stone2 {
276
+ number: 456 ,
277
+ digits: 3
278
+ }
279
+ ) ; // 456 * 1 = 456
280
+ }
281
+
282
+ #[ test]
283
+ fn test_muled_by_large_number2 ( ) {
284
+ let stone: Stone2 = "999" . parse ( ) . unwrap ( ) ;
285
+ let result = stone. muled_by ( 999 ) ;
286
+ assert_eq ! (
287
+ result,
288
+ Stone2 {
289
+ number: 998001 ,
290
+ digits: 6
291
+ }
292
+ ) ; // 999 * 999 = 998001
293
+ }
294
+
295
+ #[ test]
296
+ fn test_split ( ) {
297
+ let stone: Stone2 = "1001" . parse ( ) . unwrap ( ) ;
298
+ let result = stone. split ( ) ;
299
+ assert_eq ! ( result, [ Stone2 :: new( 10 ) , Stone2 :: new( 1 ) ] ) ;
300
+ }
136
301
}
0 commit comments