1
- use std:: {
2
- cmp:: max,
3
- fmt:: Debug ,
4
- sync:: {
5
- mpsc:: { channel, Receiver , Sender } ,
6
- Arc ,
7
- } ,
8
- } ;
1
+ use std:: { cmp:: max, fmt:: Debug , sync:: Arc } ;
9
2
10
3
use cubing:: {
11
4
alg:: { Alg , AlgNode , Move } ,
@@ -48,9 +41,9 @@ const MAX_SUPPORTED_SEARCH_DEPTH: Depth = Depth(500); // TODO: increase
48
41
// TODO: use https://doc.rust-lang.org/std/ops/enum.ControlFlow.html as a wrapper instead?
49
42
#[ allow( clippy:: enum_variant_names) ]
50
43
enum SearchRecursionResult {
51
- DoneSearching ,
52
44
ContinueSearchingDefault ,
53
45
ContinueSearchingExcludingCurrentMoveClass ,
46
+ FoundSolution ( Alg ) ,
54
47
}
55
48
56
49
struct SolutionPreviousMoves < ' a > {
@@ -103,48 +96,31 @@ impl<'a> Iterator for SolutionMovesReverseIterator<'a> {
103
96
}
104
97
}
105
98
106
- pub struct SearchSolutions {
107
- receiver : Receiver < Option < Alg > > ,
108
- done : bool ,
99
+ pub struct IterativeSearchCursor < ' a , TPuzzle : SemiGroupActionPuzzle = KPuzzle > {
100
+ search : & ' a mut IterativeDeepeningSearch < TPuzzle > ,
101
+ individual_search_data : IndividualSearchData < TPuzzle > ,
109
102
}
110
103
111
- impl SearchSolutions {
112
- pub fn construct ( ) -> ( Sender < Option < Alg > > , Self ) {
113
- // TODO: use `sync_channel` to control resumption?
114
- let ( sender, receiver) = channel :: < Option < Alg > > ( ) ;
115
- (
116
- sender,
117
- Self {
118
- receiver,
119
- done : false ,
120
- } ,
121
- )
104
+ impl < TPuzzle : SemiGroupActionPuzzle > Iterator for IterativeSearchCursor < ' _ , TPuzzle > {
105
+ type Item = Alg ;
106
+
107
+ fn next ( & mut self ) -> Option < Alg > {
108
+ self . search
109
+ . search_internal ( & mut self . individual_search_data )
122
110
}
123
111
}
124
112
125
- impl Iterator for SearchSolutions {
113
+ pub struct OwnedIterativeSearchCursor < TPuzzle : SemiGroupActionPuzzle = KPuzzle > {
114
+ search : IterativeDeepeningSearch < TPuzzle > ,
115
+ individual_search_data : IndividualSearchData < TPuzzle > ,
116
+ }
117
+
118
+ impl < TPuzzle : SemiGroupActionPuzzle > Iterator for OwnedIterativeSearchCursor < TPuzzle > {
126
119
type Item = Alg ;
127
120
128
- fn next ( & mut self ) -> Option < Self :: Item > {
129
- if self . done {
130
- None
131
- } else {
132
- let received = match self . receiver . recv ( ) {
133
- Ok ( received) => received,
134
- Err ( _) => {
135
- // TODO: this could be either a channel failure or no solutions found. We should find a way for the latter to avoid hitting this code path.
136
- self . done = true ;
137
- return None ;
138
- }
139
- } ;
140
- match received {
141
- Some ( alg) => Some ( alg) ,
142
- None => {
143
- self . done = true ;
144
- None
145
- }
146
- }
147
- }
121
+ fn next ( & mut self ) -> Option < Alg > {
122
+ self . search
123
+ . search_internal ( & mut self . individual_search_data )
148
124
}
149
125
}
150
126
@@ -255,13 +231,57 @@ impl IndividualSearchOptions {
255
231
}
256
232
257
233
struct IndividualSearchData < TPuzzle : SemiGroupActionPuzzle > {
234
+ search_pattern : TPuzzle :: Pattern ,
258
235
individual_search_options : IndividualSearchOptions ,
259
236
recursive_work_tracker : RecursiveWorkTracker ,
260
237
num_solutions_sofar : usize ,
261
- solution_sender : Sender < Option < Alg > > ,
262
238
individual_search_adaptations : IndividualSearchAdaptations < TPuzzle > ,
263
239
}
264
240
241
+ impl < TPuzzle : SemiGroupActionPuzzle > IndividualSearchData < TPuzzle > {
242
+ /// Note that search is pull-based. You must call `.next()` (or invoke
243
+ /// something that does) on the return value for the search to begine.
244
+ pub fn new (
245
+ search : & mut IterativeDeepeningSearch < TPuzzle > ,
246
+ search_pattern : & TPuzzle :: Pattern ,
247
+ mut individual_search_options : IndividualSearchOptions ,
248
+ individual_search_adaptations : IndividualSearchAdaptations < TPuzzle > ,
249
+ ) -> Self {
250
+ // TODO: do validation more consisten tly.
251
+ if let Some ( min_depth) = individual_search_options. min_depth_inclusive {
252
+ if min_depth > MAX_SUPPORTED_SEARCH_DEPTH {
253
+ search
254
+ . api_data
255
+ . search_logger
256
+ . write_error ( "Min depth too large, capping at maximum." ) ;
257
+ individual_search_options. min_depth_inclusive = Some ( MAX_SUPPORTED_SEARCH_DEPTH ) ;
258
+ }
259
+ }
260
+ if let Some ( max_depth) = individual_search_options. max_depth_exclusive {
261
+ if max_depth > MAX_SUPPORTED_SEARCH_DEPTH {
262
+ search
263
+ . api_data
264
+ . search_logger
265
+ . write_error ( "Max depth too large, capping at maximum." ) ;
266
+ individual_search_options. max_depth_exclusive = Some ( MAX_SUPPORTED_SEARCH_DEPTH ) ;
267
+ }
268
+ }
269
+
270
+ let search_pattern = search_pattern. clone ( ) ;
271
+
272
+ Self {
273
+ search_pattern,
274
+ individual_search_options,
275
+ recursive_work_tracker : RecursiveWorkTracker :: new (
276
+ "Search" . to_owned ( ) ,
277
+ search. api_data . search_logger . clone ( ) ,
278
+ ) ,
279
+ num_solutions_sofar : 0 ,
280
+ individual_search_adaptations,
281
+ }
282
+ }
283
+ }
284
+
265
285
pub struct IterativeDeepeningSearchAPIData < TPuzzle : SemiGroupActionPuzzle > {
266
286
pub search_generators : SearchGenerators < TPuzzle > ,
267
287
pub canonical_fsm : CanonicalFSM < TPuzzle > , // TODO: move this into `SearchAdaptations`
@@ -420,55 +440,86 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
420
440
} )
421
441
}
422
442
423
- pub fn search_with_default_individual_search_adaptations (
424
- & mut self ,
443
+ /// Note that search is pull-based. You must call `.next()` (or invoke
444
+ /// something that does) on the return value for the search to begine.
445
+ pub fn search_with_default_individual_search_adaptations < ' a > (
446
+ & ' a mut self ,
425
447
search_pattern : & TPuzzle :: Pattern ,
426
448
individual_search_options : IndividualSearchOptions ,
427
- ) -> SearchSolutions {
449
+ ) -> IterativeSearchCursor < ' a , TPuzzle > {
428
450
self . search (
429
451
search_pattern,
430
452
individual_search_options,
431
453
Default :: default ( ) ,
432
454
)
433
455
}
434
456
435
- pub fn search (
436
- & mut self ,
457
+ /// Note that search is pull-based. You must call `.next()` (or invoke
458
+ /// something that does) on the return value for the search to begine.
459
+ pub fn owned_search_with_default_individual_search_adaptations (
460
+ self ,
437
461
search_pattern : & TPuzzle :: Pattern ,
438
- mut individual_search_options : IndividualSearchOptions ,
462
+ individual_search_options : IndividualSearchOptions ,
463
+ ) -> OwnedIterativeSearchCursor < TPuzzle > {
464
+ self . owned_search (
465
+ search_pattern,
466
+ individual_search_options,
467
+ Default :: default ( ) ,
468
+ )
469
+ }
470
+
471
+ /// Note that search is pull-based. You must call `.next()` (or invoke
472
+ /// something that does) on the return value for the search to begine.
473
+ pub fn search < ' a > (
474
+ & ' a mut self ,
475
+ search_pattern : & TPuzzle :: Pattern ,
476
+ individual_search_options : IndividualSearchOptions ,
439
477
individual_search_adaptations : IndividualSearchAdaptations < TPuzzle > ,
440
- ) -> SearchSolutions {
441
- // TODO: do validation more consistently.
442
- if let Some ( min_depth) = individual_search_options. min_depth_inclusive {
443
- if min_depth > MAX_SUPPORTED_SEARCH_DEPTH {
444
- self . api_data
445
- . search_logger
446
- . write_error ( "Min depth too large, capping at maximum." ) ;
447
- individual_search_options. min_depth_inclusive = Some ( MAX_SUPPORTED_SEARCH_DEPTH ) ;
448
- }
449
- }
450
- if let Some ( max_depth) = individual_search_options. max_depth_exclusive {
451
- if max_depth > MAX_SUPPORTED_SEARCH_DEPTH {
452
- self . api_data
453
- . search_logger
454
- . write_error ( "Max depth too large, capping at maximum." ) ;
455
- individual_search_options. max_depth_exclusive = Some ( MAX_SUPPORTED_SEARCH_DEPTH ) ;
456
- }
478
+ ) -> IterativeSearchCursor < ' a , TPuzzle > {
479
+ let individual_search_data = IndividualSearchData :: new (
480
+ self ,
481
+ search_pattern,
482
+ individual_search_options,
483
+ individual_search_adaptations,
484
+ ) ;
485
+ IterativeSearchCursor {
486
+ search : self ,
487
+ individual_search_data,
457
488
}
489
+ }
458
490
459
- let ( solution_sender, search_solutions) = SearchSolutions :: construct ( ) ;
460
- let mut individual_search_data = IndividualSearchData {
491
+ /// Note that search is pull-based. You must call `.next()` (or invoke
492
+ /// something that does) on the return value for the search to begine.
493
+ pub fn owned_search (
494
+ mut self ,
495
+ search_pattern : & TPuzzle :: Pattern ,
496
+ individual_search_options : IndividualSearchOptions ,
497
+ individual_search_adaptations : IndividualSearchAdaptations < TPuzzle > ,
498
+ ) -> OwnedIterativeSearchCursor < TPuzzle > {
499
+ let individual_search_data = IndividualSearchData :: new (
500
+ & mut self ,
501
+ search_pattern,
461
502
individual_search_options,
462
- recursive_work_tracker : RecursiveWorkTracker :: new (
463
- "Search" . to_owned ( ) ,
464
- self . api_data . search_logger . clone ( ) ,
465
- ) ,
466
- num_solutions_sofar : 0 ,
467
- solution_sender,
468
503
individual_search_adaptations,
469
- } ;
504
+ ) ;
505
+ OwnedIterativeSearchCursor {
506
+ search : self ,
507
+ individual_search_data,
508
+ }
509
+ }
470
510
471
- let search_pattern = search_pattern. clone ( ) ;
511
+ // TODO: ideally the return should be represented by a fallible iterator (since it can fail caller-provided input deep in the stack).
512
+ fn search_internal (
513
+ & mut self ,
514
+ individual_search_data : & mut IndividualSearchData < TPuzzle > ,
515
+ ) -> Option < Alg > {
516
+ if individual_search_data. num_solutions_sofar
517
+ >= individual_search_data
518
+ . individual_search_options
519
+ . get_min_num_solutions ( )
520
+ {
521
+ return None ;
522
+ }
472
523
473
524
let ( initial_search_depth, initial_depth_continuation_condition) = {
474
525
let options_min_depth = individual_search_data
@@ -506,7 +557,10 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
506
557
let mut initial_depth_continuation_condition = initial_depth_continuation_condition;
507
558
508
559
// TODO: combine `KPatternStack` with `SolutionMoves`?
509
- let mut pattern_stack = PatternStack :: new ( self . api_data . tpuzzle . clone ( ) , search_pattern) ;
560
+ let mut pattern_stack = PatternStack :: new (
561
+ self . api_data . tpuzzle . clone ( ) ,
562
+ individual_search_data. search_pattern . clone ( ) ,
563
+ ) ;
510
564
for remaining_depth in * initial_search_depth
511
565
..* individual_search_data
512
566
. individual_search_options
@@ -535,7 +589,7 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
535
589
)
536
590
. expect ( "TODO: invalid canonical FSM pre-moves." ) ;
537
591
let recursion_result = self . recurse (
538
- & mut individual_search_data,
592
+ individual_search_data,
539
593
& mut pattern_stack,
540
594
initial_state,
541
595
remaining_depth,
@@ -545,12 +599,13 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
545
599
individual_search_data
546
600
. recursive_work_tracker
547
601
. finish_latest_depth ( ) ;
548
- if let SearchRecursionResult :: DoneSearching = recursion_result {
549
- break ;
602
+ if let SearchRecursionResult :: FoundSolution ( alg ) = recursion_result {
603
+ return Some ( alg ) ;
550
604
}
551
605
initial_depth_continuation_condition = ContinuationCondition :: None ;
552
606
}
553
- search_solutions
607
+
608
+ None
554
609
}
555
610
556
611
fn recurse (
@@ -666,13 +721,13 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
666
721
pattern_stack. pop ( ) ;
667
722
668
723
match recursive_result {
669
- SearchRecursionResult :: DoneSearching => {
670
- return SearchRecursionResult :: DoneSearching ;
671
- }
672
724
SearchRecursionResult :: ContinueSearchingDefault => { }
673
725
SearchRecursionResult :: ContinueSearchingExcludingCurrentMoveClass => {
674
726
break ;
675
727
}
728
+ SearchRecursionResult :: FoundSolution ( alg) => {
729
+ return SearchRecursionResult :: FoundSolution ( alg)
730
+ }
676
731
}
677
732
}
678
733
}
@@ -798,23 +853,7 @@ impl<TPuzzle: SemiGroupActionPuzzle> IterativeDeepeningSearch<TPuzzle> {
798
853
799
854
let alg = Alg :: from ( & solution_moves) ;
800
855
individual_search_data. num_solutions_sofar += 1 ;
801
- individual_search_data
802
- . solution_sender
803
- . send ( Some ( alg) )
804
- . expect ( "Internal error: could not send solution" ) ;
805
- if individual_search_data. num_solutions_sofar
806
- >= individual_search_data
807
- . individual_search_options
808
- . get_min_num_solutions ( )
809
- {
810
- individual_search_data
811
- . solution_sender
812
- . send ( None )
813
- . expect ( "Internal error: could not send end of search" ) ;
814
- SearchRecursionResult :: DoneSearching
815
- } else {
816
- SearchRecursionResult :: ContinueSearchingDefault
817
- }
856
+ SearchRecursionResult :: FoundSolution ( alg)
818
857
}
819
858
820
859
fn is_target_pattern ( & self , current_pattern : & TPuzzle :: Pattern ) -> bool {
0 commit comments