solverforge_solver/heuristic/selector/
mimic.rs

1//! Mimic selectors for synchronized selection across multiple selectors.
2//!
3//! Mimic selectors enable multiple selectors to select the same element in lockstep.
4//! This is essential for:
5//! - Nearby selection: Get the "origin" entity that was already selected
6//! - Coordinated moves: Ensure multiple parts of a move reference the same entity
7//!
8//! # Architecture
9//!
10//! - [`MimicRecordingEntitySelector`]: Wraps a child selector and records each selected entity
11//! - [`MimicReplayingEntitySelector`]: Replays the entity recorded by a recording selector
12
13use std::fmt::Debug;
14use std::sync::{Arc, RwLock};
15
16use solverforge_core::domain::PlanningSolution;
17use solverforge_scoring::ScoreDirector;
18
19use super::entity::{EntityReference, EntitySelector};
20
21/// Shared state between recording and replaying selectors.
22#[derive(Debug, Default)]
23struct MimicState {
24    /// Whether hasNext has been called on the recorder.
25    has_next_recorded: bool,
26    /// The result of the last hasNext call.
27    has_next: bool,
28    /// Whether next has been called on the recorder.
29    next_recorded: bool,
30    /// The last recorded entity reference.
31    recorded_entity: Option<EntityReference>,
32}
33
34/// Handle for sharing mimic state between recording and replaying selectors.
35#[derive(Debug, Clone)]
36pub struct MimicRecorder {
37    state: Arc<RwLock<MimicState>>,
38    /// Identifier for debugging.
39    id: String,
40}
41
42impl MimicRecorder {
43    /// Creates a new mimic recorder with the given identifier.
44    pub fn new(id: impl Into<String>) -> Self {
45        Self {
46            state: Arc::new(RwLock::new(MimicState::default())),
47            id: id.into(),
48        }
49    }
50
51    /// Records a has_next result.
52    fn record_has_next(&self, has_next: bool) {
53        let mut state = self.state.write().unwrap();
54        state.has_next_recorded = true;
55        state.has_next = has_next;
56        state.next_recorded = false;
57        state.recorded_entity = None;
58    }
59
60    /// Records a next result.
61    fn record_next(&self, entity: EntityReference) {
62        let mut state = self.state.write().unwrap();
63        state.has_next_recorded = true;
64        state.has_next = true;
65        state.next_recorded = true;
66        state.recorded_entity = Some(entity);
67    }
68
69    /// Gets the recorded has_next state.
70    pub fn get_has_next(&self) -> Option<bool> {
71        let state = self.state.read().unwrap();
72        if state.has_next_recorded {
73            Some(state.has_next)
74        } else {
75            None
76        }
77    }
78
79    /// Gets the recorded entity.
80    pub fn get_recorded_entity(&self) -> Option<EntityReference> {
81        let state = self.state.read().unwrap();
82        if state.next_recorded {
83            state.recorded_entity
84        } else {
85            None
86        }
87    }
88
89    /// Returns the ID of this recorder.
90    pub fn id(&self) -> &str {
91        &self.id
92    }
93
94    /// Resets the state for a new iteration.
95    pub fn reset(&self) {
96        *self.state.write().unwrap() = MimicState::default();
97    }
98}
99
100/// An entity selector that records each selected entity for replay by other selectors.
101///
102/// This is used to synchronize selection across multiple selectors. The recording
103/// selector wraps a child selector and broadcasts each selected entity to all
104/// replaying selectors that share the same recorder.
105pub struct MimicRecordingEntitySelector<S: PlanningSolution> {
106    /// The child selector that actually selects entities.
107    child: Box<dyn EntitySelector<S>>,
108    /// The recorder that broadcasts selections.
109    recorder: MimicRecorder,
110}
111
112impl<S: PlanningSolution> MimicRecordingEntitySelector<S> {
113    /// Creates a new recording selector wrapping the given child selector.
114    pub fn new(child: Box<dyn EntitySelector<S>>, recorder: MimicRecorder) -> Self {
115        Self { child, recorder }
116    }
117
118    /// Returns the recorder for creating replaying selectors.
119    pub fn recorder(&self) -> MimicRecorder {
120        self.recorder.clone()
121    }
122}
123
124impl<S: PlanningSolution> Debug for MimicRecordingEntitySelector<S> {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        f.debug_struct("MimicRecordingEntitySelector")
127            .field("child", &self.child)
128            .field("recorder_id", &self.recorder.id)
129            .finish()
130    }
131}
132
133impl<S: PlanningSolution> EntitySelector<S> for MimicRecordingEntitySelector<S> {
134    fn iter<'a>(
135        &'a self,
136        score_director: &'a dyn ScoreDirector<S>,
137    ) -> Box<dyn Iterator<Item = EntityReference> + 'a> {
138        // Reset for new iteration
139        self.recorder.reset();
140
141        let child_iter = self.child.iter(score_director);
142        Box::new(RecordingIterator {
143            inner: child_iter,
144            recorder: &self.recorder,
145        })
146    }
147
148    fn size(&self, score_director: &dyn ScoreDirector<S>) -> usize {
149        self.child.size(score_director)
150    }
151
152    fn is_never_ending(&self) -> bool {
153        self.child.is_never_ending()
154    }
155}
156
157/// Iterator that records each entity as it's yielded.
158struct RecordingIterator<'a> {
159    inner: Box<dyn Iterator<Item = EntityReference> + 'a>,
160    recorder: &'a MimicRecorder,
161}
162
163impl<'a> Iterator for RecordingIterator<'a> {
164    type Item = EntityReference;
165
166    fn next(&mut self) -> Option<Self::Item> {
167        let next = self.inner.next();
168        match next {
169            Some(entity) => {
170                self.recorder.record_next(entity);
171                Some(entity)
172            }
173            None => {
174                self.recorder.record_has_next(false);
175                None
176            }
177        }
178    }
179}
180
181/// An entity selector that replays the last entity recorded by a recording selector.
182///
183/// This selector always yields exactly one entity (the last one recorded) or no entities
184/// if the recording selector hasn't recorded anything yet.
185pub struct MimicReplayingEntitySelector {
186    /// The recorder to replay from.
187    recorder: MimicRecorder,
188}
189
190impl MimicReplayingEntitySelector {
191    /// Creates a new replaying selector that replays from the given recorder.
192    pub fn new(recorder: MimicRecorder) -> Self {
193        Self { recorder }
194    }
195}
196
197impl Debug for MimicReplayingEntitySelector {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        f.debug_struct("MimicReplayingEntitySelector")
200            .field("recorder_id", &self.recorder.id)
201            .finish()
202    }
203}
204
205impl<S: PlanningSolution> EntitySelector<S> for MimicReplayingEntitySelector {
206    fn iter<'a>(
207        &'a self,
208        _score_director: &'a dyn ScoreDirector<S>,
209    ) -> Box<dyn Iterator<Item = EntityReference> + 'a> {
210        Box::new(ReplayingIterator {
211            recorder: &self.recorder,
212            returned: false,
213        })
214    }
215
216    fn size(&self, _score_director: &dyn ScoreDirector<S>) -> usize {
217        // At most one entity is returned
218        if self.recorder.get_recorded_entity().is_some() {
219            1
220        } else {
221            0
222        }
223    }
224
225    fn is_never_ending(&self) -> bool {
226        false
227    }
228}
229
230/// Iterator that replays a single recorded entity.
231struct ReplayingIterator<'a> {
232    recorder: &'a MimicRecorder,
233    returned: bool,
234}
235
236impl<'a> Iterator for ReplayingIterator<'a> {
237    type Item = EntityReference;
238
239    fn next(&mut self) -> Option<Self::Item> {
240        if self.returned {
241            return None;
242        }
243
244        // Check if something was recorded
245        match self.recorder.get_recorded_entity() {
246            Some(entity) => {
247                self.returned = true;
248                Some(entity)
249            }
250            None => {
251                // Check has_next to provide better error handling
252                match self.recorder.get_has_next() {
253                    Some(false) => None, // Recording selector exhausted
254                    Some(true) => panic!(
255                        "MimicReplayingEntitySelector: Recording selector's hasNext() was true \
256                         but next() was never called. Ensure the recording selector's iterator \
257                         is advanced before using the replaying selector."
258                    ),
259                    None => panic!(
260                        "MimicReplayingEntitySelector: No recording found. \
261                         The recording selector must be iterated before the replaying selector."
262                    ),
263                }
264            }
265        }
266    }
267}
268
269#[cfg(test)]
270mod tests {
271    use super::*;
272    use crate::heuristic::selector::entity::FromSolutionEntitySelector;
273    use solverforge_core::domain::{EntityDescriptor, SolutionDescriptor, TypedEntityExtractor};
274    use solverforge_core::score::SimpleScore;
275    use solverforge_scoring::SimpleScoreDirector;
276    use std::any::TypeId;
277
278    #[allow(dead_code)]
279    #[derive(Clone, Debug)]
280    struct Queen {
281        id: i64,
282        row: Option<i32>,
283    }
284
285    #[derive(Clone, Debug)]
286    struct NQueensSolution {
287        queens: Vec<Queen>,
288        score: Option<SimpleScore>,
289    }
290
291    impl PlanningSolution for NQueensSolution {
292        type Score = SimpleScore;
293
294        fn score(&self) -> Option<Self::Score> {
295            self.score
296        }
297
298        fn set_score(&mut self, score: Option<Self::Score>) {
299            self.score = score;
300        }
301    }
302
303    fn get_queens(s: &NQueensSolution) -> &Vec<Queen> {
304        &s.queens
305    }
306
307    fn get_queens_mut(s: &mut NQueensSolution) -> &mut Vec<Queen> {
308        &mut s.queens
309    }
310
311    fn create_test_director(
312        n: usize,
313    ) -> SimpleScoreDirector<NQueensSolution, impl Fn(&NQueensSolution) -> SimpleScore> {
314        let queens: Vec<_> = (0..n)
315            .map(|i| Queen {
316                id: i as i64,
317                row: Some(i as i32),
318            })
319            .collect();
320
321        let solution = NQueensSolution {
322            queens,
323            score: None,
324        };
325
326        let extractor = Box::new(TypedEntityExtractor::new(
327            "Queen",
328            "queens",
329            get_queens,
330            get_queens_mut,
331        ));
332        let entity_desc = EntityDescriptor::new("Queen", TypeId::of::<Queen>(), "queens")
333            .with_extractor(extractor);
334
335        let descriptor =
336            SolutionDescriptor::new("NQueensSolution", TypeId::of::<NQueensSolution>())
337                .with_entity(entity_desc);
338
339        SimpleScoreDirector::with_calculator(solution, descriptor, |_| SimpleScore::of(0))
340    }
341
342    #[test]
343    fn test_mimic_recording_selector() {
344        let director = create_test_director(3);
345
346        // Verify entity IDs
347        let solution = director.working_solution();
348        for (i, queen) in solution.queens.iter().enumerate() {
349            assert_eq!(queen.id, i as i64);
350        }
351
352        let recorder = MimicRecorder::new("test");
353        let child = Box::new(FromSolutionEntitySelector::new(0));
354        let recording = MimicRecordingEntitySelector::new(child, recorder);
355
356        let entities: Vec<_> = recording.iter(&director).collect();
357        assert_eq!(entities.len(), 3);
358        assert_eq!(entities[0], EntityReference::new(0, 0));
359        assert_eq!(entities[1], EntityReference::new(0, 1));
360        assert_eq!(entities[2], EntityReference::new(0, 2));
361    }
362
363    #[test]
364    fn test_mimic_replaying_selector() {
365        let director = create_test_director(3);
366
367        let recorder = MimicRecorder::new("test");
368        let child = Box::new(FromSolutionEntitySelector::new(0));
369        let recording = MimicRecordingEntitySelector::new(child, recorder.clone());
370        let replaying = MimicReplayingEntitySelector::new(recorder);
371
372        // Iterate through recording selector
373        let mut recording_iter = recording.iter(&director);
374
375        // First entity recorded
376        let first = recording_iter.next().unwrap();
377        assert_eq!(first, EntityReference::new(0, 0));
378
379        // Replaying should yield the same entity
380        let replayed: Vec<_> = replaying.iter(&director).collect();
381        assert_eq!(replayed.len(), 1);
382        assert_eq!(replayed[0], EntityReference::new(0, 0));
383
384        // Move to second entity
385        let second = recording_iter.next().unwrap();
386        assert_eq!(second, EntityReference::new(0, 1));
387
388        // Replaying should now yield the second entity
389        let replayed: Vec<_> = replaying.iter(&director).collect();
390        assert_eq!(replayed.len(), 1);
391        assert_eq!(replayed[0], EntityReference::new(0, 1));
392    }
393
394    #[test]
395    fn test_mimic_synchronized_iteration() {
396        let director = create_test_director(3);
397
398        let recorder = MimicRecorder::new("test");
399        let child = Box::new(FromSolutionEntitySelector::new(0));
400        let recording = MimicRecordingEntitySelector::new(child, recorder.clone());
401        let replaying = MimicReplayingEntitySelector::new(recorder);
402
403        // Simulate how this would be used in a move selector:
404        // For each recorded entity, get the replayed entity
405        for recorded in recording.iter(&director) {
406            let replayed: Vec<_> = replaying.iter(&director).collect();
407            assert_eq!(replayed.len(), 1);
408            assert_eq!(replayed[0], recorded);
409        }
410    }
411
412    #[test]
413    fn test_mimic_empty_selector() {
414        let director = create_test_director(0);
415
416        let recorder = MimicRecorder::new("test");
417        let child = Box::new(FromSolutionEntitySelector::new(0));
418        let recording = MimicRecordingEntitySelector::new(child, recorder.clone());
419        let replaying = MimicReplayingEntitySelector::new(recorder);
420
421        // Recording selector is empty
422        let entities: Vec<_> = recording.iter(&director).collect();
423        assert_eq!(entities.len(), 0);
424
425        // Replaying should also be empty
426        let replayed: Vec<_> = replaying.iter(&director).collect();
427        assert_eq!(replayed.len(), 0);
428    }
429}