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
116pub struct QueuedEntityPlacer<S, V, ES, VS>
127where
128 S: PlanningSolution,
129 ES: EntitySelector<S>,
130 VS: ValueSelector<S, V>,
131{
132 entity_selector: ES,
134 value_selector: VS,
136 getter: fn(&S, usize) -> Option<V>,
138 setter: fn(&mut S, usize, Option<V>),
140 variable_name: &'static str,
142 descriptor_index: usize,
144 allows_unassigned: bool,
146 _phantom: PhantomData<fn() -> V>,
147}
148
149impl<S, V, ES, VS> QueuedEntityPlacer<S, V, ES, VS>
150where
151 S: PlanningSolution,
152 ES: EntitySelector<S>,
153 VS: ValueSelector<S, V>,
154{
155 pub fn new(
156 entity_selector: ES,
157 value_selector: VS,
158 getter: fn(&S, usize) -> Option<V>,
159 setter: fn(&mut S, usize, Option<V>),
160 descriptor_index: usize,
161 variable_name: &'static str,
162 ) -> Self {
163 Self {
164 entity_selector,
165 value_selector,
166 getter,
167 setter,
168 variable_name,
169 descriptor_index,
170 allows_unassigned: false,
171 _phantom: PhantomData,
172 }
173 }
174
175 pub fn with_allows_unassigned(mut self, allows_unassigned: bool) -> Self {
176 self.allows_unassigned = allows_unassigned;
177 self
178 }
179}
180
181impl<S, V, ES, VS> Debug for QueuedEntityPlacer<S, V, ES, VS>
182where
183 S: PlanningSolution,
184 ES: EntitySelector<S> + Debug,
185 VS: ValueSelector<S, V> + Debug,
186{
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 f.debug_struct("QueuedEntityPlacer")
189 .field("entity_selector", &self.entity_selector)
190 .field("value_selector", &self.value_selector)
191 .field("variable_name", &self.variable_name)
192 .field("allows_unassigned", &self.allows_unassigned)
193 .finish()
194 }
195}
196
197impl<S, V, ES, VS> EntityPlacer<S, ChangeMove<S, V>> for QueuedEntityPlacer<S, V, ES, VS>
198where
199 S: PlanningSolution,
200 V: Clone + PartialEq + Send + Sync + Debug + 'static,
201 ES: EntitySelector<S>,
202 VS: ValueSelector<S, V>,
203{
204 fn get_placements<D: Director<S>>(
205 &self,
206 score_director: &D,
207 ) -> Vec<Placement<S, ChangeMove<S, V>>> {
208 let variable_name = self.variable_name;
209 let descriptor_index = self.descriptor_index;
210 let getter = self.getter;
211 let setter = self.setter;
212 let allows_unassigned = self.allows_unassigned;
213
214 self.entity_selector
215 .iter(score_director)
216 .filter_map(|entity_ref| {
217 let current_value =
219 getter(score_director.working_solution(), entity_ref.entity_index);
220
221 if current_value.is_some() {
223 return None;
224 }
225
226 let moves: Vec<ChangeMove<S, V>> = self
228 .value_selector
229 .iter_typed(
230 score_director,
231 entity_ref.descriptor_index,
232 entity_ref.entity_index,
233 )
234 .map(|value| {
235 ChangeMove::new(
236 entity_ref.entity_index,
237 Some(value),
238 getter,
239 setter,
240 variable_name,
241 descriptor_index,
242 )
243 })
244 .collect();
245
246 if moves.is_empty() {
247 None
248 } else {
249 Some(
250 Placement::new(entity_ref, moves)
251 .with_keep_current_legal(allows_unassigned),
252 )
253 }
254 })
255 .collect()
256 }
257}
258
259pub struct SortedEntityPlacer<S, M, Inner>
302where
303 S: PlanningSolution,
304 M: Move<S>,
305 Inner: EntityPlacer<S, M>,
306{
307 inner: Inner,
308 comparator: fn(&S, usize, usize) -> std::cmp::Ordering,
310 _phantom: PhantomData<fn() -> (S, M)>,
311}
312
313impl<S, M, Inner> SortedEntityPlacer<S, M, Inner>
314where
315 S: PlanningSolution,
316 M: Move<S>,
317 Inner: EntityPlacer<S, M>,
318{
319 pub fn new(inner: Inner, comparator: fn(&S, usize, usize) -> std::cmp::Ordering) -> Self {
325 Self {
326 inner,
327 comparator,
328 _phantom: PhantomData,
329 }
330 }
331}
332
333impl<S, M, Inner> Debug for SortedEntityPlacer<S, M, Inner>
334where
335 S: PlanningSolution,
336 M: Move<S>,
337 Inner: EntityPlacer<S, M>,
338{
339 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
340 f.debug_struct("SortedEntityPlacer")
341 .field("inner", &self.inner)
342 .finish()
343 }
344}
345
346impl<S, M, Inner> EntityPlacer<S, M> for SortedEntityPlacer<S, M, Inner>
347where
348 S: PlanningSolution,
349 M: Move<S>,
350 Inner: EntityPlacer<S, M>,
351{
352 fn get_placements<D: Director<S>>(&self, score_director: &D) -> Vec<Placement<S, M>> {
353 let mut placements = self.inner.get_placements(score_director);
354 let solution = score_director.working_solution();
355 let cmp = self.comparator;
356
357 placements.sort_by(|a, b| {
358 cmp(
359 solution,
360 a.entity_ref.entity_index,
361 b.entity_ref.entity_index,
362 )
363 });
364
365 placements
366 }
367}
368
369#[cfg(test)]
370#[path = "placer_tests.rs"]
371mod tests;