1
1
use std:: fmt:: Display ;
2
2
3
- #[ derive( Clone , Copy , Debug , PartialEq ) ]
3
+ #[ derive( Clone , Copy , Debug , Eq , PartialEq , Ord , PartialOrd ) ]
4
4
enum Condition {
5
5
Operational = '.' as isize ,
6
6
Damaged = '#' as isize ,
@@ -29,13 +29,18 @@ impl Display for Condition {
29
29
}
30
30
31
31
// State when searching for arrangements
32
- #[ derive( Clone , Debug ) ]
32
+ #[ derive( Clone , Debug , Eq ) ]
33
33
struct State {
34
34
conditions : Vec < Condition > ,
35
35
unchecked_condition : Option < Condition > ,
36
36
last_damaged_count : usize ,
37
37
last_damaged_spring_groups_index : usize ,
38
38
invalid : bool ,
39
+
40
+ // Number of states that collapsed into this one because
41
+ // they had the same last_damaged_spring_groups_index, last_damaged_count and
42
+ // last condition.
43
+ num_similar : usize ,
39
44
}
40
45
41
46
impl State {
@@ -46,6 +51,7 @@ impl State {
46
51
last_damaged_count : 0 ,
47
52
last_damaged_spring_groups_index : 0 ,
48
53
invalid : false ,
54
+ num_similar : 0 ,
49
55
}
50
56
}
51
57
fn and ( & self , c : Condition ) -> State {
@@ -61,6 +67,7 @@ impl State {
61
67
last_damaged_count : self . last_damaged_count ,
62
68
last_damaged_spring_groups_index : self . last_damaged_spring_groups_index ,
63
69
invalid : false ,
70
+ num_similar : self . num_similar ,
64
71
}
65
72
}
66
73
@@ -160,7 +167,7 @@ impl State {
160
167
impl Display for State {
161
168
fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
162
169
let cond_str = std:: str:: from_utf8 ( & self . conditions . iter ( ) . map ( |c| * c as u8 ) . collect :: < Vec < u8 > > ( ) ) . unwrap ( ) . to_string ( ) ;
163
- let r = write ! ( f, "\" {}\" (ldc = {}, i = {}, invalid = {}" , cond_str, self . last_damaged_count, self . last_damaged_spring_groups_index, self . invalid) ;
170
+ let r = write ! ( f, "\" {}\" (ldc = {}, i = {}, invalid = {}, similar = {} " , cond_str, self . last_damaged_count, self . last_damaged_spring_groups_index, self . invalid, self . num_similar ) ;
164
171
if r. is_err ( ) {
165
172
panic ! ( "cannot format state" ) ;
166
173
}
@@ -172,6 +179,80 @@ impl Display for State {
172
179
}
173
180
}
174
181
182
+ impl std:: cmp:: PartialEq for State {
183
+ fn eq ( & self , other : & Self ) -> bool {
184
+ if self . unchecked_condition . is_some ( ) || other. unchecked_condition . is_some ( ) {
185
+ panic ! ( "cannot compare states with unchecked conditions" ) ;
186
+ }
187
+
188
+ // Easy things:
189
+ if self . invalid != other. invalid {
190
+ return false ;
191
+ }
192
+ if self . conditions . len ( ) != other. conditions . len ( ) {
193
+ return false ;
194
+ }
195
+ if self . last_damaged_count != other. last_damaged_count {
196
+ return false ;
197
+ }
198
+ if self . last_damaged_spring_groups_index != other. last_damaged_spring_groups_index {
199
+ return false ;
200
+ }
201
+
202
+ // Last condition must be the same:
203
+ match ( self . conditions . last ( ) , other. conditions . last ( ) ) {
204
+ ( Some ( last) , Some ( other_last) ) => {
205
+ if last != other_last {
206
+ return false ;
207
+ }
208
+ } ,
209
+ ( None , None ) => { } ,
210
+ _ => return false ,
211
+ }
212
+
213
+ // NB: We're not comparing num_similar, as that is just an internal
214
+ // multiplier.
215
+ true
216
+ }
217
+ }
218
+
219
+ impl std:: cmp:: PartialOrd for State {
220
+ fn partial_cmp ( & self , other : & Self ) -> Option < std:: cmp:: Ordering > {
221
+ if self . unchecked_condition . is_some ( ) || other. unchecked_condition . is_some ( ) {
222
+ panic ! ( "cannot compare states with unchecked conditions" ) ;
223
+ }
224
+
225
+ // First: Compare the parts we also use in the partial equality checks.
226
+ match self . invalid . partial_cmp ( & other. invalid ) {
227
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
228
+ ord => return ord,
229
+ }
230
+ match self . conditions . len ( ) . partial_cmp ( & other. conditions . len ( ) ) {
231
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
232
+ ord => return ord,
233
+ }
234
+ match self . last_damaged_count . partial_cmp ( & other. last_damaged_count ) {
235
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
236
+ ord => return ord,
237
+ }
238
+ match self . last_damaged_spring_groups_index . partial_cmp ( & other. last_damaged_spring_groups_index ) {
239
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
240
+ ord => return ord,
241
+ }
242
+ match self . conditions . last ( ) . partial_cmp ( & other. conditions . last ( ) ) {
243
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
244
+ ord => return ord,
245
+ }
246
+
247
+ // Rest "just" needs to be sane enough.
248
+ match self . conditions . partial_cmp ( & other. conditions ) {
249
+ Some ( core:: cmp:: Ordering :: Equal ) => { }
250
+ ord => return ord,
251
+ }
252
+ self . num_similar . partial_cmp ( & other. num_similar )
253
+ }
254
+ }
255
+
175
256
struct ConditionRecord {
176
257
conditions : Vec < Condition > ,
177
258
damaged_spring_groups : Vec < usize > ,
@@ -188,7 +269,7 @@ impl ConditionRecord {
188
269
}
189
270
190
271
pub fn num_arrangements ( & self ) -> usize {
191
- Self :: arrangements ( & self . conditions , & self . damaged_spring_groups , self . repeat , false ) . len ( )
272
+ Self :: arrangements ( & self . conditions , & self . damaged_spring_groups , self . repeat , false ) . drain ( .. ) . fold ( 0 , |acc , s| acc + s . num_similar + 1 )
192
273
}
193
274
194
275
fn process_state ( damaged_spring_groups : & [ usize ] , s : State , c : Condition , print : bool ) -> Vec < State > {
@@ -220,7 +301,7 @@ impl ConditionRecord {
220
301
result
221
302
}
222
303
223
- fn arrangements ( conditions : & [ Condition ] , damaged_spring_groups : & [ usize ] , repeat : usize , print : bool ) -> Vec < String > {
304
+ fn arrangements ( conditions : & [ Condition ] , damaged_spring_groups : & [ usize ] , repeat : usize , print : bool ) -> Vec < State > {
224
305
// Stupid logic: Each '?' can be either '.' or '#'. So, try both, and then proceed
225
306
// and see if _at the end_. Essentially we're building a matching automaton here?????!?!?!!
226
307
// Each state is the conditions we found.
@@ -231,18 +312,39 @@ impl ConditionRecord {
231
312
states. push ( State :: empty ( ) ) ;
232
313
for r in 0 ..repeat {
233
314
for ( i, c) in conditions. iter ( ) . enumerate ( ) {
234
- let next_states = & states
315
+ let next_states = & mut states
235
316
. drain ( ..)
236
317
. flat_map ( |s| Self :: process_state ( & repeated_damaged_spring_groups, s, * c, print) )
237
318
. collect :: < Vec < _ > > ( ) ;
238
- if next_states. is_empty ( ) {
239
- println ! ( "no more states, can return empty early" ) ;
240
- return vec ! [ ] ;
241
- }
242
- * states = next_states. to_vec ( ) ;
319
+
320
+ let next_states_len = next_states. len ( ) ;
321
+
322
+ // Collapse states: Sort them, then drop equal ones and bump the num_similar counter. At the
323
+ // end we just count these counters when summing up.
324
+ states. sort_by ( |a, b| a. partial_cmp ( b) . unwrap ( ) ) ;
325
+ let collapsed_states = & next_states
326
+ . drain ( ..)
327
+ . fold ( vec ! [ ] , |mut acc : Vec < State > , s| {
328
+ if let Some ( last) = acc. last_mut ( ) {
329
+ if * last == s {
330
+ last. num_similar += s. num_similar + 1 ;
331
+ if print {
332
+ println ! ( "... {} collapsed into {}" , s, last) ;
333
+ }
334
+ } else {
335
+ acc. push ( s) ;
336
+ }
337
+ } else {
338
+ acc. push ( s) ;
339
+ }
340
+ acc
341
+ } ) ;
342
+
243
343
if print {
244
- println ! ( "{}/{}: {}/{} |states | = {:?}" , r, repeat, i, conditions. len( ) , states . len( ) ) ;
344
+ println ! ( "{}/{}: {}/{} |next_states | = {:?}, |collapsed_states| = {:?} " , r, repeat, i, conditions. len( ) , next_states_len , collapsed_states . len( ) ) ;
245
345
}
346
+
347
+ * states = collapsed_states. to_vec ( ) ;
246
348
}
247
349
248
350
// Repeating adds a unknown condition, so process that.
@@ -254,47 +356,34 @@ impl ConditionRecord {
254
356
. drain ( ..)
255
357
. flat_map ( |s| Self :: process_state ( & repeated_damaged_spring_groups, s, Condition :: Unknown , print) )
256
358
. collect :: < Vec < _ > > ( ) ;
257
- if next_states. is_empty ( ) {
258
- println ! ( "no more states, can return empty early" ) ;
259
- return vec ! [ ] ;
260
- }
261
359
262
- // Prune everything that doesn't have at least a remote chance: We should have roughly
263
- // the same number of groups in this section than existed in the original damaged_spring_groups.
264
- states. clear ( ) ;
265
- for s in next_states {
266
- // XXX: This is probably the same or related-to as last_damaged_group_index.
267
- // XXX: The problem is that we may have situations where the added Unknown actually makes some group
268
- // possible -- but I don't see yet how.
269
- let expected_num_groups = damaged_spring_groups. len ( ) * ( r + 1 ) ;
270
- if s. last_damaged_spring_groups_index >= expected_num_groups - 1 {
271
- states. push ( s. clone ( ) ) ;
272
- }
273
- }
360
+ // We could do the collapsing here, but it will happen in the next iteration anyways
361
+ // and shouldn't cost too much as we already did it just above.
362
+ * states = next_states. to_vec ( ) ;
363
+ }
274
364
275
- if print {
276
- println ! ( "{}/{}: |states after repeat| = {:?}, dropped = {}" , r , repeat , states. len ( ) , next_states . len ( ) - states . len ( ) ) ;
277
- }
365
+ if states . is_empty ( ) {
366
+ println ! ( "no more states, can return empty early" ) ;
367
+ return vec ! [ ] ;
278
368
}
279
369
}
280
370
281
371
// Filter out the invalid states
282
- let mut result = vec ! [ ] ;
372
+ let mut final_states = vec ! [ ] ;
283
373
for s in states. iter_mut ( ) {
284
374
let valid = s. check_constraints ( & repeated_damaged_spring_groups, false , false ) ;
285
375
if valid {
286
376
// println!("state = {}: counted, valid at full", s);
287
- let cond_str = std:: str:: from_utf8 ( & s. conditions . iter ( ) . map ( |c| * c as u8 ) . collect :: < Vec < u8 > > ( ) ) . unwrap ( ) . to_string ( ) ;
288
- result. push ( cond_str) ;
377
+ final_states. push ( s. to_owned ( ) ) ;
289
378
} else {
290
379
// println!("state = {}: pruned, invalid at full", s);
291
380
}
292
381
}
293
382
294
383
if print {
295
- println ! ( "|result| = {:?}" , result . len ( ) ) ;
384
+ println ! ( "|result| = {:?}" , final_states ) ;
296
385
}
297
- result
386
+ final_states
298
387
}
299
388
300
389
fn parse_state ( s : & str ) -> Vec < Condition > {
@@ -427,7 +516,7 @@ impl std::str::FromStr for ConditionRecord {
427
516
}
428
517
}
429
518
430
- fn num_arrangements ( input : & str , repeat : usize ) -> usize {
519
+ fn num_arrangements ( input : & str , repeat : usize , concurrent : u8 ) -> usize {
431
520
let thread_count_arc = std:: sync:: Arc :: new ( ( std:: sync:: Mutex :: new ( 0u8 ) , std:: sync:: Condvar :: new ( ) ) ) ;
432
521
433
522
let mut v = vec ! [ ] ;
@@ -438,7 +527,7 @@ fn num_arrangements(input: &str, repeat: usize) -> usize {
438
527
let ( num, cvar) = & * thread_count;
439
528
440
529
let mut start = cvar
441
- . wait_while ( num. lock ( ) . unwrap ( ) , |start| * start >= 32 )
530
+ . wait_while ( num. lock ( ) . unwrap ( ) , |start| * start >= concurrent )
442
531
. unwrap ( ) ;
443
532
* start += 1 ;
444
533
drop ( start) ;
@@ -469,7 +558,7 @@ pub fn main() {
469
558
match std:: fs:: read_to_string ( "day12.input" ) {
470
559
Ok ( input) => {
471
560
// println!("num_arrangements = {}", num_arrangements(&input, 1));
472
- println ! ( "num_arrangements (part 2) = {}" , num_arrangements( & input, 5 ) ) ;
561
+ println ! ( "num_arrangements (part 2) = {}" , num_arrangements( & input, 5 , 32 ) ) ;
473
562
} ,
474
563
Err ( reason) => println ! ( "error = {}" , reason)
475
564
}
@@ -566,17 +655,17 @@ mod tests {
566
655
assert_eq ! ( "????.######..#####. 1,6,5" . parse:: <ConditionRecord >( ) . unwrap( ) . num_arrangements( ) , 4 ) ;
567
656
assert_eq ! ( "?###???????? 3,2,1" . parse:: <ConditionRecord >( ) . unwrap( ) . num_arrangements( ) , 10 ) ;
568
657
569
- assert_eq ! ( num_arrangements( DATA , 1 ) , 21 )
658
+ assert_eq ! ( num_arrangements( DATA , 1 , 1 ) , 21 )
570
659
}
571
660
572
661
#[ test]
573
662
fn part1_tests ( ) {
574
663
//assert_eq!("???.#??#.??? 1,1,2,1".parse::<ConditionRecord>().unwrap().num_arrangements(), 10);
575
664
let cr = "##???#??#?????????#? 11,6" . parse :: < ConditionRecord > ( ) . unwrap ( ) ;
576
- assert_eq ! ( ConditionRecord :: arrangements( & cr. conditions, & cr. damaged_spring_groups, 1 , false ) , & [
665
+ assert_eq ! ( ConditionRecord :: arrangements( & cr. conditions, & cr. damaged_spring_groups, 1 , false ) . len ( ) , [
577
666
"###########..######." ,
578
667
"###########...######" ,
579
- ] ) ;
668
+ ] . len ( ) ) ;
580
669
}
581
670
582
671
#[ test]
@@ -594,7 +683,17 @@ mod tests {
594
683
assert_eq ! ( "????.######..#####. 1,6,5" . parse:: <ConditionRecord >( ) . unwrap( ) . repeat( 5 ) . num_arrangements( ) , 2500 ) ;
595
684
assert_eq ! ( "?###???????? 3,2,1" . parse:: <ConditionRecord >( ) . unwrap( ) . repeat( 5 ) . num_arrangements( ) , 506250 ) ;
596
685
597
- assert_eq ! ( num_arrangements( DATA , 5 ) , 525152 )
686
+ assert_eq ! ( num_arrangements( DATA , 5 , 1 ) , 525152 )
687
+ }
688
+
689
+ #[ test]
690
+ fn part2_simple ( ) {
691
+ let cr = "????.#...#... 4,1,1" . parse :: < ConditionRecord > ( ) . unwrap ( ) ;
692
+ let states = ConditionRecord :: arrangements ( & cr. conditions , & cr. damaged_spring_groups , 5 , true ) ;
693
+ for s in & states {
694
+ println ! ( "state = {}" , s) ;
695
+ }
696
+ assert_eq ! ( states. iter( ) . fold( 0 , |acc, s| acc + s. num_similar + 1 ) , 16 ) ;
598
697
}
599
698
600
699
#[ test]
0 commit comments