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
+
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) ) ;
10
22
}
11
23
12
24
// Most significant digit is last,
@@ -24,12 +36,12 @@ fn part1(stones: Vec<Stone>) -> usize {
24
36
. len ( )
25
37
}
26
38
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 ) ) ) ;
30
42
31
43
for _ in 0 ..75 {
32
- stone_map = accumulate_into_hashmap (
44
+ stone_map = accumulate (
33
45
stone_map
34
46
. into_iter ( )
35
47
. flat_map ( |( stone, count) | blink_at ( stone) . into_iter ( ) . map ( move |s| ( s, count) ) ) ,
@@ -38,20 +50,6 @@ fn part2(stones: Vec<Stone>) -> usize {
38
50
stone_map. iter ( ) . map ( |( _, count) | count) . sum ( )
39
51
}
40
52
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
53
fn blink_at ( stone : Stone ) -> Vec < Stone > {
56
54
match stone {
57
55
zero if zero. 0 . is_empty ( ) => vec ! [ Stone :: from( [ 1 ] ) ] ,
@@ -61,17 +59,6 @@ fn blink_at(stone: Stone) -> Vec<Stone> {
61
59
}
62
60
63
61
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
62
fn new ( digits : Vec < u8 > ) -> Stone {
76
63
Stone ( digits) . trimmed ( )
77
64
}
@@ -108,29 +95,208 @@ impl Stone {
108
95
}
109
96
}
110
97
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
+
111
231
#[ cfg( test) ]
112
232
mod tests {
113
233
use super :: * ;
114
234
115
235
#[ test]
116
236
fn test_muled_by_zero ( ) {
117
- let stone = Stone :: parse ( "123" ) ;
237
+ let stone: Stone = "123" . parse ( ) . unwrap ( ) ;
118
238
let result = stone. muled_by ( 0 ) ;
119
239
assert_eq ! ( result. 0 , vec![ 0 , 0 , 0 ] ) ; // 123 * 0 = 0
120
240
}
121
241
122
242
#[ test]
123
243
fn test_muled_by_one ( ) {
124
- let stone = Stone :: parse ( "456" ) ;
244
+ let stone: Stone = "456" . parse ( ) . unwrap ( ) ;
125
245
let result = stone. muled_by ( 1 ) ;
126
246
assert_eq ! ( result. 0 , vec![ 6 , 5 , 4 ] ) ; // 456 * 1 = 456
127
247
}
128
248
129
249
#[ test]
130
250
fn test_muled_by_large_number ( ) {
131
- let stone = Stone :: parse ( "999" ) ;
251
+ let stone: Stone = "999" . parse ( ) . unwrap ( ) ;
132
252
let result = stone. muled_by ( 999 ) ;
133
253
// 999 * 999 = 998001
134
254
assert_eq ! ( result. 0 , vec![ 1 , 0 , 0 , 8 , 9 , 9 ] ) ;
135
255
}
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
+ }
136
302
}
0 commit comments