1use std::fmt::Debug;
8use std::marker::PhantomData;
9
10use solverforge_core::domain::PlanningSolution;
11use solverforge_scoring::Director;
12
13use crate::heuristic::r#move::{ChangeMove, Move};
14use crate::heuristic::selector::{EntityReference, EntitySelector, ValueSelector};
15
16use super::ConstructionSlotId;
17
18pub struct Placement<S, M>
25where
26 S: PlanningSolution,
27 M: Move<S>,
28{
29 pub entity_ref: EntityReference,
31 pub moves: Vec<M>,
33 keep_current_legal: bool,
35 slot_id: Option<ConstructionSlotId>,
36 _phantom: PhantomData<fn() -> S>,
37}
38
39impl<S, M> Placement<S, M>
40where
41 S: PlanningSolution,
42 M: Move<S>,
43{
44 pub fn new(entity_ref: EntityReference, moves: Vec<M>) -> Self {
45 Self {
46 entity_ref,
47 moves,
48 keep_current_legal: false,
49 slot_id: None,
50 _phantom: PhantomData,
51 }
52 }
53
54 pub fn is_empty(&self) -> bool {
55 self.moves.is_empty()
56 }
57
58 pub fn with_keep_current_legal(mut self, legal: bool) -> Self {
59 self.keep_current_legal = legal;
60 self
61 }
62
63 pub fn keep_current_legal(&self) -> bool {
64 self.keep_current_legal
65 }
66
67 pub(crate) fn with_slot_id(mut self, slot_id: ConstructionSlotId) -> Self {
68 self.slot_id = Some(slot_id);
69 self
70 }
71
72 pub(crate) fn slot_id(&self) -> Option<ConstructionSlotId> {
73 self.slot_id
74 }
75
76 pub fn take_move(&mut self, index: usize) -> M {
80 self.moves.swap_remove(index)
81 }
82}
83
84impl<S, M> Debug for Placement<S, M>
85where
86 S: PlanningSolution,
87 M: Move<S>,
88{
89 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
90 f.debug_struct("Placement")
91 .field("entity_ref", &self.entity_ref)
92 .field("move_count", &self.moves.len())
93 .field("keep_current_legal", &self.keep_current_legal)
94 .field("slot_id", &self.slot_id)
95 .finish()
96 }
97}
98
99pub trait EntityPlacer<S, M>: Send + Debug
108where
109 S: PlanningSolution,
110 M: Move<S>,
111{
112 fn get_placements<D: Director<S>>(&self, score_director: &D) -> Vec<Placement<S, M>>;
114
115 fn get_next_placement<D, IsCompleted>(
118 &self,
119 score_director: &D,
120 mut is_completed: IsCompleted,
121 ) -> Option<(Placement<S, M>, u64)>
122 where
123 D: Director<S>,
124 IsCompleted: FnMut(usize, usize) -> bool,
125 {
126 let mut selected = None;
127 let mut generated_moves = 0u64;
128
129 for placement in self.get_placements(score_director) {
130 if let Some(slot_id) = placement.slot_id() {
131 if is_completed(slot_id.binding_index(), slot_id.entity_index()) {
132 continue;
133 }
134 }
135 generated_moves = generated_moves
136 .saturating_add(u64::try_from(placement.moves.len()).unwrap_or(u64::MAX));
137 if selected.is_none() {
138 selected = Some(placement);
139 }
140 }
141
142 selected.map(|placement| (placement, generated_moves))
143 }
144}
145
146pub struct QueuedEntityPlacer<S, V, ES, VS>
157where
158 S: PlanningSolution,
159 ES: EntitySelector<S>,
160 VS: ValueSelector<S, V>,
161{
162 entity_selector: ES,
164 value_selector: VS,
166 getter: fn(&S, usize, usize) -> Option<V>,
168 setter: fn(&mut S, usize, usize, Option<V>),
170 variable_index: usize,
171 variable_name: &'static str,
173 descriptor_index: usize,
175 allows_unassigned: bool,
177 _phantom: PhantomData<fn() -> V>,
178}
179
180impl<S, V, ES, VS> QueuedEntityPlacer<S, V, ES, VS>
181where
182 S: PlanningSolution,
183 ES: EntitySelector<S>,
184 VS: ValueSelector<S, V>,
185{
186 pub fn new(
187 entity_selector: ES,
188 value_selector: VS,
189 getter: fn(&S, usize, usize) -> Option<V>,
190 setter: fn(&mut S, usize, usize, Option<V>),
191 descriptor_index: usize,
192 variable_index: usize,
193 variable_name: &'static str,
194 ) -> Self {
195 Self {
196 entity_selector,
197 value_selector,
198 getter,
199 setter,
200 variable_index,
201 variable_name,
202 descriptor_index,
203 allows_unassigned: false,
204 _phantom: PhantomData,
205 }
206 }
207
208 pub fn with_allows_unassigned(mut self, allows_unassigned: bool) -> Self {
209 self.allows_unassigned = allows_unassigned;
210 self
211 }
212}
213
214impl<S, V, ES, VS> Debug for QueuedEntityPlacer<S, V, ES, VS>
215where
216 S: PlanningSolution,
217 ES: EntitySelector<S> + Debug,
218 VS: ValueSelector<S, V> + Debug,
219{
220 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
221 f.debug_struct("QueuedEntityPlacer")
222 .field("entity_selector", &self.entity_selector)
223 .field("value_selector", &self.value_selector)
224 .field("variable_name", &self.variable_name)
225 .field("allows_unassigned", &self.allows_unassigned)
226 .finish()
227 }
228}
229
230impl<S, V, ES, VS> EntityPlacer<S, ChangeMove<S, V>> for QueuedEntityPlacer<S, V, ES, VS>
231where
232 S: PlanningSolution,
233 V: Clone + PartialEq + Send + Sync + Debug + 'static,
234 ES: EntitySelector<S>,
235 VS: ValueSelector<S, V>,
236{
237 fn get_placements<D: Director<S>>(
238 &self,
239 score_director: &D,
240 ) -> Vec<Placement<S, ChangeMove<S, V>>> {
241 let variable_name = self.variable_name;
242 let descriptor_index = self.descriptor_index;
243 let getter = self.getter;
244 let setter = self.setter;
245 let variable_index = self.variable_index;
246 let allows_unassigned = self.allows_unassigned;
247
248 self.entity_selector
249 .iter(score_director)
250 .filter_map(|entity_ref| {
251 let current_value = getter(
253 score_director.working_solution(),
254 entity_ref.entity_index,
255 variable_index,
256 );
257
258 if current_value.is_some() {
260 return None;
261 }
262
263 let moves: Vec<ChangeMove<S, V>> = self
265 .value_selector
266 .iter(
267 score_director,
268 entity_ref.descriptor_index,
269 entity_ref.entity_index,
270 )
271 .map(|value| {
272 ChangeMove::new(
273 entity_ref.entity_index,
274 Some(value),
275 getter,
276 setter,
277 variable_index,
278 variable_name,
279 descriptor_index,
280 )
281 })
282 .collect();
283
284 if moves.is_empty() {
285 None
286 } else {
287 Some(
288 Placement::new(entity_ref, moves)
289 .with_keep_current_legal(allows_unassigned),
290 )
291 }
292 })
293 .collect()
294 }
295}
296
297pub struct SortedEntityPlacer<S, M, Inner>
340where
341 S: PlanningSolution,
342 M: Move<S>,
343 Inner: EntityPlacer<S, M>,
344{
345 inner: Inner,
346 comparator: fn(&S, usize, usize) -> std::cmp::Ordering,
348 _phantom: PhantomData<fn() -> (S, M)>,
349}
350
351impl<S, M, Inner> SortedEntityPlacer<S, M, Inner>
352where
353 S: PlanningSolution,
354 M: Move<S>,
355 Inner: EntityPlacer<S, M>,
356{
357 pub fn new(inner: Inner, comparator: fn(&S, usize, usize) -> std::cmp::Ordering) -> Self {
363 Self {
364 inner,
365 comparator,
366 _phantom: PhantomData,
367 }
368 }
369}
370
371impl<S, M, Inner> Debug for SortedEntityPlacer<S, M, Inner>
372where
373 S: PlanningSolution,
374 M: Move<S>,
375 Inner: EntityPlacer<S, M>,
376{
377 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
378 f.debug_struct("SortedEntityPlacer")
379 .field("inner", &self.inner)
380 .finish()
381 }
382}
383
384impl<S, M, Inner> EntityPlacer<S, M> for SortedEntityPlacer<S, M, Inner>
385where
386 S: PlanningSolution,
387 M: Move<S>,
388 Inner: EntityPlacer<S, M>,
389{
390 fn get_placements<D: Director<S>>(&self, score_director: &D) -> Vec<Placement<S, M>> {
391 let mut placements = self.inner.get_placements(score_director);
392 let solution = score_director.working_solution();
393 let cmp = self.comparator;
394
395 placements.sort_by(|a, b| {
396 cmp(
397 solution,
398 a.entity_ref.entity_index,
399 b.entity_ref.entity_index,
400 )
401 });
402
403 placements
404 }
405}
406
407#[cfg(test)]
408mod tests;