Skip to main content

solverforge_solver/heuristic/selector/
mimic.rs

1/* Mimic selectors for synchronized selection across multiple selectors.
2
3Mimic selectors enable multiple selectors to select the same element in lockstep.
4This 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*/
13
14use std::fmt::Debug;
15use std::sync::{Arc, Mutex};
16
17use solverforge_core::domain::PlanningSolution;
18use solverforge_scoring::Director;
19
20use super::entity::{EntityReference, EntitySelector};
21
22/* Shared state between recording and replaying selectors.
23
24A plain owned value behind one `Arc<Mutex<..>>`: the recording selector
25writes, the replaying selector reads, and the state is freed when the last
26handle drops. No leaked allocations, no manual refcounts, no unsafe impls.
27The lock is uncontended in the sequential record-then-replay flow.
28*/
29#[derive(Debug, Default, Clone, Copy)]
30struct MimicState {
31    // Whether hasNext has been called on the recorder.
32    has_next_recorded: bool,
33    // The result of the last hasNext call.
34    has_next: bool,
35    // Whether next has been called on the recorder.
36    next_recorded: bool,
37    // The last recorded entity reference.
38    recorded_entity: Option<EntityReference>,
39}
40
41/// Handle for sharing mimic state between recording and replaying selectors.
42#[derive(Clone)]
43pub struct MimicRecorder {
44    shared: Arc<Mutex<MimicState>>,
45    // Identifier for debugging.
46    id: String,
47}
48
49impl MimicRecorder {
50    pub fn new(id: impl Into<String>) -> Self {
51        Self {
52            shared: Arc::new(Mutex::new(MimicState::default())),
53            id: id.into(),
54        }
55    }
56
57    // Records a has_next result.
58    fn record_has_next(&self, has_next: bool) {
59        *self.shared.lock().unwrap() = MimicState {
60            has_next_recorded: true,
61            has_next,
62            next_recorded: false,
63            recorded_entity: None,
64        };
65    }
66
67    // Records a next result.
68    fn record_next(&self, entity: EntityReference) {
69        *self.shared.lock().unwrap() = MimicState {
70            has_next_recorded: true,
71            has_next: true,
72            next_recorded: true,
73            recorded_entity: Some(entity),
74        };
75    }
76
77    pub fn get_has_next(&self) -> Option<bool> {
78        let state = self.shared.lock().unwrap();
79        if state.has_next_recorded {
80            Some(state.has_next)
81        } else {
82            None
83        }
84    }
85
86    pub fn get_recorded_entity(&self) -> Option<EntityReference> {
87        let state = self.shared.lock().unwrap();
88        if state.next_recorded {
89            state.recorded_entity
90        } else {
91            None
92        }
93    }
94
95    pub fn id(&self) -> &str {
96        &self.id
97    }
98
99    /// Resets the state for a new iteration.
100    pub fn reset(&self) {
101        *self.shared.lock().unwrap() = MimicState::default();
102    }
103}
104
105impl Debug for MimicRecorder {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_struct("MimicRecorder")
108            .field("id", &self.id)
109            .field("state", &*self.shared.lock().unwrap())
110            .finish()
111    }
112}
113
114/// An entity selector that records each selected entity for replay by other selectors.
115///
116/// This is used to synchronize selection across multiple selectors. The recording
117/// selector wraps a child selector and broadcasts each selected entity to all
118/// replaying selectors that share the same recorder.
119///
120/// # Zero-Erasure Design
121///
122/// The child entity selector `ES` is stored as a concrete generic type parameter,
123/// eliminating virtual dispatch overhead when iterating over entities.
124pub struct MimicRecordingEntitySelector<S, ES> {
125    // The child selector that actually selects entities (zero-erasure).
126    child: ES,
127    // The recorder that broadcasts selections.
128    recorder: MimicRecorder,
129    // Marker for solution type.
130    _phantom: std::marker::PhantomData<fn() -> S>,
131}
132
133impl<S, ES> MimicRecordingEntitySelector<S, ES> {
134    pub fn new(child: ES, recorder: MimicRecorder) -> Self {
135        Self {
136            child,
137            recorder,
138            _phantom: std::marker::PhantomData,
139        }
140    }
141
142    pub fn recorder(&self) -> MimicRecorder {
143        self.recorder.clone()
144    }
145}
146
147impl<S: PlanningSolution, ES: Debug> Debug for MimicRecordingEntitySelector<S, ES> {
148    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149        f.debug_struct("MimicRecordingEntitySelector")
150            .field("child", &self.child)
151            .field("recorder_id", &self.recorder.id)
152            .finish()
153    }
154}
155
156impl<S, ES> EntitySelector<S> for MimicRecordingEntitySelector<S, ES>
157where
158    S: PlanningSolution,
159    ES: EntitySelector<S>,
160{
161    fn iter<'a, D: Director<S>>(
162        &'a self,
163        score_director: &D,
164    ) -> impl Iterator<Item = EntityReference> + 'a {
165        // Reset for new iteration
166        self.recorder.reset();
167
168        let child_iter = self.child.iter(score_director);
169        RecordingIterator {
170            inner: child_iter,
171            recorder: &self.recorder,
172        }
173    }
174
175    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
176        self.child.size(score_director)
177    }
178
179    fn is_never_ending(&self) -> bool {
180        self.child.is_never_ending()
181    }
182}
183
184// Iterator that records each entity as it's yielded.
185struct RecordingIterator<'a, I> {
186    inner: I,
187    recorder: &'a MimicRecorder,
188}
189
190impl<'a, I: Iterator<Item = EntityReference>> Iterator for RecordingIterator<'a, I> {
191    type Item = EntityReference;
192
193    fn next(&mut self) -> Option<Self::Item> {
194        let next = self.inner.next();
195        match next {
196            Some(entity) => {
197                self.recorder.record_next(entity);
198                Some(entity)
199            }
200            None => {
201                self.recorder.record_has_next(false);
202                None
203            }
204        }
205    }
206}
207
208/// An entity selector that replays the last entity recorded by a recording selector.
209///
210/// This selector always yields exactly one entity (the last one recorded) or no entities
211/// if the recording selector hasn't recorded anything yet.
212pub struct MimicReplayingEntitySelector {
213    // The recorder to replay from.
214    recorder: MimicRecorder,
215}
216
217impl MimicReplayingEntitySelector {
218    pub fn new(recorder: MimicRecorder) -> Self {
219        Self { recorder }
220    }
221}
222
223impl Debug for MimicReplayingEntitySelector {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        f.debug_struct("MimicReplayingEntitySelector")
226            .field("recorder_id", &self.recorder.id)
227            .finish()
228    }
229}
230
231impl<S: PlanningSolution> EntitySelector<S> for MimicReplayingEntitySelector {
232    fn iter<'a, D: Director<S>>(
233        &'a self,
234        _score_director: &D,
235    ) -> impl Iterator<Item = EntityReference> + 'a {
236        ReplayingIterator {
237            recorder: &self.recorder,
238            returned: false,
239        }
240    }
241
242    fn size<D: Director<S>>(&self, _score_director: &D) -> usize {
243        // At most one entity is returned
244        if self.recorder.get_recorded_entity().is_some() {
245            1
246        } else {
247            0
248        }
249    }
250
251    fn is_never_ending(&self) -> bool {
252        false
253    }
254}
255
256// Iterator that replays a single recorded entity.
257struct ReplayingIterator<'a> {
258    recorder: &'a MimicRecorder,
259    returned: bool,
260}
261
262impl<'a> Iterator for ReplayingIterator<'a> {
263    type Item = EntityReference;
264
265    fn next(&mut self) -> Option<Self::Item> {
266        if self.returned {
267            return None;
268        }
269
270        // Check if something was recorded
271        match self.recorder.get_recorded_entity() {
272            Some(entity) => {
273                self.returned = true;
274                Some(entity)
275            }
276            None => {
277                // Check has_next to provide better error handling
278                match self.recorder.get_has_next() {
279                    Some(false) => None, // Recording selector exhausted
280                    Some(true) => panic!(
281                        "MimicReplayingEntitySelector: Recording selector's hasNext() was true \
282                         but next() was never called. Ensure the recording selector's iterator \
283                         is advanced before using the replaying selector."
284                    ),
285                    None => panic!(
286                        "MimicReplayingEntitySelector: No recording found. \
287                         The recording selector must be iterated before the replaying selector."
288                    ),
289                }
290            }
291        }
292    }
293}