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::move_selector::{CandidateId, MoveCandidateRef, MoveCursor};
15use crate::heuristic::selector::{EntityReference, EntitySelector, ValueSelector};
16use crate::stats::CandidateTracePullToken;
17
18use super::{ConstructionGroupSlotId, ConstructionSlotId};
19
20#[derive(Clone, Debug, Default)]
21pub(crate) struct ConstructionTarget {
22 scalar_slots: Vec<ConstructionSlotId>,
23 group_slot: Option<ConstructionGroupSlotId>,
24}
25
26impl ConstructionTarget {
27 pub(crate) fn new() -> Self {
28 Self::default()
29 }
30
31 pub(crate) fn with_scalar_slots(mut self, mut scalar_slots: Vec<ConstructionSlotId>) -> Self {
32 scalar_slots.sort_unstable();
33 scalar_slots.dedup();
34 self.scalar_slots = scalar_slots;
35 self
36 }
37
38 pub(crate) fn with_group_slot(mut self, group_slot: ConstructionGroupSlotId) -> Self {
39 self.group_slot = Some(group_slot);
40 self
41 }
42
43 pub(crate) fn scalar_slots(&self) -> &[ConstructionSlotId] {
44 &self.scalar_slots
45 }
46
47 pub(crate) fn group_slot(&self) -> Option<&ConstructionGroupSlotId> {
48 self.group_slot.as_ref()
49 }
50
51 pub(crate) fn is_empty(&self) -> bool {
52 self.scalar_slots.is_empty() && self.group_slot.is_none()
53 }
54}
55
56pub struct Placement<S, M, C = crate::heuristic::selector::move_selector::ArenaMoveCursor<S, M>>
63where
64 S: PlanningSolution,
65 M: Move<S>,
66 C: MoveCursor<S, M>,
67{
68 pub entity_ref: EntityReference,
70 candidates: C,
71 keep_current_legal: bool,
73 target: ConstructionTarget,
74 candidate_target: fn(&C, CandidateId) -> Option<&ConstructionTarget>,
75 candidate_trace_tokens: Vec<(CandidateId, CandidateTracePullToken)>,
78 candidate_scores: Vec<(CandidateId, S::Score)>,
79 _phantom: PhantomData<fn() -> (S, M)>,
80}
81
82impl<S, M, C> Placement<S, M, C>
83where
84 S: PlanningSolution,
85 M: Move<S>,
86 C: MoveCursor<S, M>,
87{
88 pub fn new(entity_ref: EntityReference, candidates: C) -> Self {
89 Self {
90 entity_ref,
91 candidates,
92 keep_current_legal: false,
93 target: ConstructionTarget::new(),
94 candidate_target: |_, _| None,
95 candidate_trace_tokens: Vec::new(),
96 candidate_scores: Vec::new(),
97 _phantom: PhantomData,
98 }
99 }
100
101 pub fn with_keep_current_legal(mut self, legal: bool) -> Self {
102 self.keep_current_legal = legal;
103 self
104 }
105
106 pub fn keep_current_legal(&self) -> bool {
107 self.keep_current_legal
108 }
109
110 pub(crate) fn with_slot_id(mut self, slot_id: ConstructionSlotId) -> Self {
111 self.target = self.target.with_scalar_slots(vec![slot_id]);
112 self
113 }
114
115 pub(crate) fn with_scalar_slots(mut self, mut scalar_slots: Vec<ConstructionSlotId>) -> Self {
116 scalar_slots.sort_unstable();
117 scalar_slots.dedup();
118 self.target = self.target.with_scalar_slots(scalar_slots);
119 self
120 }
121
122 pub(crate) fn with_group_slot(mut self, group_slot: ConstructionGroupSlotId) -> Self {
123 self.target = self.target.with_group_slot(group_slot);
124 self
125 }
126
127 pub(crate) fn with_candidate_target(
128 mut self,
129 candidate_target: fn(&C, CandidateId) -> Option<&ConstructionTarget>,
130 ) -> Self {
131 self.candidate_target = candidate_target;
132 self
133 }
134
135 pub(crate) fn construction_target(&self) -> &ConstructionTarget {
136 &self.target
137 }
138
139 pub(crate) fn construction_target_for_move(
140 &self,
141 candidate_id: CandidateId,
142 ) -> &ConstructionTarget {
143 (self.candidate_target)(&self.candidates, candidate_id).unwrap_or(&self.target)
144 }
145
146 pub fn candidates(&self) -> &C {
147 &self.candidates
148 }
149
150 pub fn candidates_mut(&mut self) -> &mut C {
151 &mut self.candidates
152 }
153
154 pub fn take_move(&mut self, candidate_id: CandidateId) -> M {
155 self.candidates.take_candidate(candidate_id)
156 }
157
158 pub(crate) fn record_candidate_trace_token(
159 &mut self,
160 candidate_id: CandidateId,
161 token: CandidateTracePullToken,
162 ) {
163 self.candidate_trace_tokens.push((candidate_id, token));
164 }
165
166 pub(crate) fn candidate_trace_token(
167 &self,
168 candidate_id: CandidateId,
169 ) -> Option<CandidateTracePullToken> {
170 self.candidate_trace_tokens
171 .iter()
172 .find_map(|(recorded_id, token)| (*recorded_id == candidate_id).then_some(*token))
173 }
174
175 pub(crate) fn record_candidate_score(&mut self, candidate_id: CandidateId, score: S::Score) {
176 self.candidate_scores.push((candidate_id, score));
177 }
178
179 pub(crate) fn candidate_score(&self, candidate_id: CandidateId) -> Option<S::Score>
180 where
181 S::Score: Copy,
182 {
183 self.candidate_scores
184 .iter()
185 .find_map(|(recorded_id, score)| (*recorded_id == candidate_id).then_some(*score))
186 }
187}
188
189impl<S, M, C> Debug for Placement<S, M, C>
190where
191 S: PlanningSolution,
192 M: Move<S>,
193 C: MoveCursor<S, M>,
194{
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 f.debug_struct("Placement")
197 .field("entity_ref", &self.entity_ref)
198 .field("keep_current_legal", &self.keep_current_legal)
199 .field("target", &self.target)
200 .finish()
201 }
202}
203
204pub trait EntityPlacerCursor<S, M>
213where
214 S: PlanningSolution,
215 M: Move<S>,
216{
217 type CandidateCursor: MoveCursor<S, M>;
218
219 fn next_placement<D, IsCompleted, ShouldStop>(
220 &mut self,
221 score_director: &D,
222 is_completed: IsCompleted,
223 should_stop: ShouldStop,
224 ) -> Option<Placement<S, M, Self::CandidateCursor>>
225 where
226 D: Director<S>,
227 IsCompleted: FnMut(&Placement<S, M, Self::CandidateCursor>) -> bool,
228 ShouldStop: FnMut() -> bool;
229}
230
231pub trait EntityPlacer<S, M>: Send + Debug
232where
233 S: PlanningSolution,
234 M: Move<S>,
235{
236 type Cursor<'a>: EntityPlacerCursor<S, M> + 'a
237 where
238 Self: 'a;
239
240 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a>;
241}
242
243include!("placer/queued.rs");
244pub struct SortedEntityPlacer<S, M, Inner>
287where
288 S: PlanningSolution,
289 M: Move<S>,
290 Inner: EntityPlacer<S, M>,
291{
292 inner: Inner,
293 comparator: fn(&S, usize, usize) -> std::cmp::Ordering,
295 _phantom: PhantomData<fn() -> (S, M)>,
296}
297
298impl<S, M, Inner> SortedEntityPlacer<S, M, Inner>
299where
300 S: PlanningSolution,
301 M: Move<S>,
302 Inner: EntityPlacer<S, M>,
303{
304 pub fn new(inner: Inner, comparator: fn(&S, usize, usize) -> std::cmp::Ordering) -> Self {
310 Self {
311 inner,
312 comparator,
313 _phantom: PhantomData,
314 }
315 }
316}
317
318impl<S, M, Inner> Debug for SortedEntityPlacer<S, M, Inner>
319where
320 S: PlanningSolution,
321 M: Move<S>,
322 Inner: EntityPlacer<S, M>,
323{
324 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
325 f.debug_struct("SortedEntityPlacer")
326 .field("inner", &self.inner)
327 .finish()
328 }
329}
330
331type InnerPlacementCandidateCursor<'a, S, M, Inner> =
332 <<Inner as EntityPlacer<S, M>>::Cursor<'a> as EntityPlacerCursor<S, M>>::CandidateCursor;
333
334pub struct SortedEntityPlacerCursor<'a, S, M, Inner>
335where
336 S: PlanningSolution,
337 M: Move<S>,
338 Inner: EntityPlacer<S, M> + 'a,
339{
340 inner: Inner::Cursor<'a>,
341 comparator: fn(&S, usize, usize) -> std::cmp::Ordering,
342 placements:
343 Option<std::vec::IntoIter<Placement<S, M, InnerPlacementCandidateCursor<'a, S, M, Inner>>>>,
344}
345
346impl<'a, S, M, Inner> EntityPlacerCursor<S, M> for SortedEntityPlacerCursor<'a, S, M, Inner>
347where
348 S: PlanningSolution,
349 M: Move<S>,
350 Inner: EntityPlacer<S, M> + 'a,
351{
352 type CandidateCursor = InnerPlacementCandidateCursor<'a, S, M, Inner>;
353
354 fn next_placement<D, IsCompleted, ShouldStop>(
355 &mut self,
356 score_director: &D,
357 mut is_completed: IsCompleted,
358 mut should_stop: ShouldStop,
359 ) -> Option<Placement<S, M, Self::CandidateCursor>>
360 where
361 D: Director<S>,
362 IsCompleted: FnMut(&Placement<S, M, Self::CandidateCursor>) -> bool,
363 ShouldStop: FnMut() -> bool,
364 {
365 if self.placements.is_none() {
366 let mut placements = Vec::new();
367 while let Some(placement) =
368 self.inner
369 .next_placement(score_director, |_| false, &mut should_stop)
370 {
371 placements.push(placement);
372 }
373 let solution = score_director.working_solution();
374 let comparator = self.comparator;
375 placements.sort_by(|left, right| {
376 comparator(
377 solution,
378 left.entity_ref.entity_index,
379 right.entity_ref.entity_index,
380 )
381 });
382 self.placements = Some(placements.into_iter());
383 }
384
385 let placements = self
386 .placements
387 .as_mut()
388 .expect("sorted placement cursor must be initialized");
389 while !should_stop() {
390 let placement = placements.next()?;
391 if !is_completed(&placement) {
392 return Some(placement);
393 }
394 }
395 None
396 }
397}
398
399impl<S, M, Inner> EntityPlacer<S, M> for SortedEntityPlacer<S, M, Inner>
400where
401 S: PlanningSolution,
402 M: Move<S>,
403 Inner: EntityPlacer<S, M>,
404{
405 type Cursor<'a>
406 = SortedEntityPlacerCursor<'a, S, M, Inner>
407 where
408 Self: 'a;
409
410 fn open_cursor<'a, D: Director<S>>(&'a self, score_director: &D) -> Self::Cursor<'a> {
411 SortedEntityPlacerCursor {
412 inner: self.inner.open_cursor(score_director),
413 comparator: self.comparator,
414 placements: None,
415 }
416 }
417}
418
419#[cfg(test)]
420mod tests;