1use std::fmt::Debug;
7use std::marker::PhantomData;
8
9use solverforge_core::domain::PlanningSolution;
10use solverforge_scoring::ScoreDirector;
11
12use crate::heuristic::r#move::{ChangeMove, Move};
13use crate::heuristic::selector::{EntityReference, EntitySelector, TypedValueSelector};
14
15pub struct Placement<S, M>
22where
23 S: PlanningSolution,
24 M: Move<S>,
25{
26 pub entity_ref: EntityReference,
28 pub moves: Vec<M>,
30 _phantom: PhantomData<S>,
31}
32
33impl<S, M> Placement<S, M>
34where
35 S: PlanningSolution,
36 M: Move<S>,
37{
38 pub fn new(entity_ref: EntityReference, moves: Vec<M>) -> Self {
40 Self {
41 entity_ref,
42 moves,
43 _phantom: PhantomData,
44 }
45 }
46
47 pub fn is_empty(&self) -> bool {
49 self.moves.is_empty()
50 }
51}
52
53impl<S, M> Debug for Placement<S, M>
54where
55 S: PlanningSolution,
56 M: Move<S>,
57{
58 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59 f.debug_struct("Placement")
60 .field("entity_ref", &self.entity_ref)
61 .field("move_count", &self.moves.len())
62 .finish()
63 }
64}
65
66pub trait EntityPlacer<S, M>: Send + Debug
75where
76 S: PlanningSolution,
77 M: Move<S>,
78{
79 fn get_placements(&self, score_director: &dyn ScoreDirector<S>) -> Vec<Placement<S, M>>;
81}
82
83pub struct QueuedEntityPlacer<S, V>
92where
93 S: PlanningSolution,
94{
95 entity_selector: Box<dyn EntitySelector<S>>,
97 value_selector: Box<dyn TypedValueSelector<S, V>>,
99 getter: fn(&S, usize) -> Option<V>,
101 setter: fn(&mut S, usize, Option<V>),
103 variable_name: &'static str,
105 descriptor_index: usize,
107}
108
109impl<S, V> QueuedEntityPlacer<S, V>
110where
111 S: PlanningSolution,
112{
113 pub fn new(
115 entity_selector: Box<dyn EntitySelector<S>>,
116 value_selector: Box<dyn TypedValueSelector<S, V>>,
117 getter: fn(&S, usize) -> Option<V>,
118 setter: fn(&mut S, usize, Option<V>),
119 descriptor_index: usize,
120 variable_name: &'static str,
121 ) -> Self {
122 Self {
123 entity_selector,
124 value_selector,
125 getter,
126 setter,
127 variable_name,
128 descriptor_index,
129 }
130 }
131}
132
133impl<S, V> Debug for QueuedEntityPlacer<S, V>
134where
135 S: PlanningSolution,
136{
137 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
138 f.debug_struct("QueuedEntityPlacer")
139 .field("entity_selector", &self.entity_selector)
140 .field("value_selector", &self.value_selector)
141 .field("variable_name", &self.variable_name)
142 .finish()
143 }
144}
145
146impl<S, V> EntityPlacer<S, ChangeMove<S, V>> for QueuedEntityPlacer<S, V>
147where
148 S: PlanningSolution,
149 V: Clone + PartialEq + Send + Sync + Debug + 'static,
150{
151 fn get_placements(
152 &self,
153 score_director: &dyn ScoreDirector<S>,
154 ) -> Vec<Placement<S, ChangeMove<S, V>>> {
155 let variable_name = self.variable_name;
156 let descriptor_index = self.descriptor_index;
157 let getter = self.getter;
158 let setter = self.setter;
159
160 self.entity_selector
161 .iter(score_director)
162 .filter_map(|entity_ref| {
163 let current_value =
165 getter(score_director.working_solution(), entity_ref.entity_index);
166
167 if current_value.is_some() {
169 return None;
170 }
171
172 let moves: Vec<ChangeMove<S, V>> = self
174 .value_selector
175 .iter_typed(
176 score_director,
177 entity_ref.descriptor_index,
178 entity_ref.entity_index,
179 )
180 .map(|value| {
181 ChangeMove::new(
182 entity_ref.entity_index,
183 Some(value),
184 getter,
185 setter,
186 variable_name,
187 descriptor_index,
188 )
189 })
190 .collect();
191
192 if moves.is_empty() {
193 None
194 } else {
195 Some(Placement::new(entity_ref, moves))
196 }
197 })
198 .collect()
199 }
200}
201
202pub struct SortedEntityPlacer<S, M, Inner>
244where
245 S: PlanningSolution,
246 M: Move<S>,
247 Inner: EntityPlacer<S, M>,
248{
249 inner: Inner,
250 comparator: fn(&S, usize, usize) -> std::cmp::Ordering,
252 _phantom: PhantomData<(S, M)>,
253}
254
255impl<S, M, Inner> SortedEntityPlacer<S, M, Inner>
256where
257 S: PlanningSolution,
258 M: Move<S>,
259 Inner: EntityPlacer<S, M>,
260{
261 pub fn new(inner: Inner, comparator: fn(&S, usize, usize) -> std::cmp::Ordering) -> Self {
267 Self {
268 inner,
269 comparator,
270 _phantom: PhantomData,
271 }
272 }
273}
274
275impl<S, M, Inner> Debug for SortedEntityPlacer<S, M, Inner>
276where
277 S: PlanningSolution,
278 M: Move<S>,
279 Inner: EntityPlacer<S, M>,
280{
281 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
282 f.debug_struct("SortedEntityPlacer")
283 .field("inner", &self.inner)
284 .finish()
285 }
286}
287
288impl<S, M, Inner> EntityPlacer<S, M> for SortedEntityPlacer<S, M, Inner>
289where
290 S: PlanningSolution,
291 M: Move<S>,
292 Inner: EntityPlacer<S, M>,
293{
294 fn get_placements(&self, score_director: &dyn ScoreDirector<S>) -> Vec<Placement<S, M>> {
295 let mut placements = self.inner.get_placements(score_director);
296 let solution = score_director.working_solution();
297 let cmp = self.comparator;
298
299 placements.sort_by(|a, b| {
300 cmp(
301 solution,
302 a.entity_ref.entity_index,
303 b.entity_ref.entity_index,
304 )
305 });
306
307 placements
308 }
309}
310
311#[cfg(test)]
312mod tests {
313 use super::*;
314 use crate::heuristic::selector::{FromSolutionEntitySelector, StaticTypedValueSelector};
315 use solverforge_core::domain::{EntityDescriptor, SolutionDescriptor, TypedEntityExtractor};
316 use solverforge_core::score::SimpleScore;
317 use solverforge_scoring::SimpleScoreDirector;
318 use std::any::TypeId;
319
320 #[derive(Clone, Debug)]
321 struct Queen {
322 row: Option<i32>,
323 }
324
325 #[derive(Clone, Debug)]
326 struct NQueensSolution {
327 queens: Vec<Queen>,
328 score: Option<SimpleScore>,
329 }
330
331 impl PlanningSolution for NQueensSolution {
332 type Score = SimpleScore;
333
334 fn score(&self) -> Option<Self::Score> {
335 self.score
336 }
337
338 fn set_score(&mut self, score: Option<Self::Score>) {
339 self.score = score;
340 }
341 }
342
343 fn get_queens(s: &NQueensSolution) -> &Vec<Queen> {
344 &s.queens
345 }
346
347 fn get_queens_mut(s: &mut NQueensSolution) -> &mut Vec<Queen> {
348 &mut s.queens
349 }
350
351 fn get_queen_row(s: &NQueensSolution, idx: usize) -> Option<i32> {
353 s.queens.get(idx).and_then(|q| q.row)
354 }
355
356 fn set_queen_row(s: &mut NQueensSolution, idx: usize, v: Option<i32>) {
358 if let Some(queen) = s.queens.get_mut(idx) {
359 queen.row = v;
360 }
361 }
362
363 fn create_test_director(
364 initialized: &[bool],
365 ) -> SimpleScoreDirector<NQueensSolution, impl Fn(&NQueensSolution) -> SimpleScore> {
366 let queens: Vec<_> = initialized
367 .iter()
368 .enumerate()
369 .map(|(i, init)| Queen {
370 row: if *init { Some(i as i32) } else { None },
371 })
372 .collect();
373
374 let solution = NQueensSolution {
375 queens,
376 score: None,
377 };
378
379 let extractor = Box::new(TypedEntityExtractor::new(
380 "Queen",
381 "queens",
382 get_queens,
383 get_queens_mut,
384 ));
385 let entity_desc = EntityDescriptor::new("Queen", TypeId::of::<Queen>(), "queens")
386 .with_extractor(extractor);
387
388 let descriptor =
389 SolutionDescriptor::new("NQueensSolution", TypeId::of::<NQueensSolution>())
390 .with_entity(entity_desc);
391
392 SimpleScoreDirector::with_calculator(solution, descriptor, |_| SimpleScore::of(0))
393 }
394
395 #[test]
396 fn test_queued_placer_all_uninitialized() {
397 let director = create_test_director(&[false, false, false]);
398
399 let entity_selector = Box::new(FromSolutionEntitySelector::new(0));
400 let value_selector = Box::new(StaticTypedValueSelector::new(vec![0i32, 1, 2]));
401
402 let placer = QueuedEntityPlacer::new(
403 entity_selector,
404 value_selector,
405 get_queen_row,
406 set_queen_row,
407 0,
408 "row",
409 );
410
411 let placements = placer.get_placements(&director);
412
413 assert_eq!(placements.len(), 3);
415
416 for p in &placements {
418 assert_eq!(p.moves.len(), 3);
419 }
420 }
421
422 #[test]
423 fn test_queued_placer_some_initialized() {
424 let director = create_test_director(&[true, false, true]);
426
427 let entity_selector = Box::new(FromSolutionEntitySelector::new(0));
428 let value_selector = Box::new(StaticTypedValueSelector::new(vec![0i32, 1, 2]));
429
430 let placer = QueuedEntityPlacer::new(
431 entity_selector,
432 value_selector,
433 get_queen_row,
434 set_queen_row,
435 0,
436 "row",
437 );
438
439 let placements = placer.get_placements(&director);
440
441 assert_eq!(placements.len(), 1);
443 assert_eq!(placements[0].entity_ref.entity_index, 1);
444 }
445
446 #[test]
447 fn test_queued_placer_all_initialized() {
448 let director = create_test_director(&[true, true, true]);
449
450 let entity_selector = Box::new(FromSolutionEntitySelector::new(0));
451 let value_selector = Box::new(StaticTypedValueSelector::new(vec![0i32, 1, 2]));
452
453 let placer = QueuedEntityPlacer::new(
454 entity_selector,
455 value_selector,
456 get_queen_row,
457 set_queen_row,
458 0,
459 "row",
460 );
461
462 let placements = placer.get_placements(&director);
463
464 assert_eq!(placements.len(), 0);
466 }
467
468 #[test]
469 fn test_sorted_entity_placer_descending() {
470 let director = create_test_director(&[false, false, false]);
472
473 let entity_selector = Box::new(FromSolutionEntitySelector::new(0));
474 let value_selector = Box::new(StaticTypedValueSelector::new(vec![0i32, 1, 2]));
475
476 let inner = QueuedEntityPlacer::new(
477 entity_selector,
478 value_selector,
479 get_queen_row,
480 set_queen_row,
481 0,
482 "row",
483 );
484
485 fn descending_index(_s: &NQueensSolution, a: usize, b: usize) -> std::cmp::Ordering {
487 b.cmp(&a)
488 }
489
490 let sorted = SortedEntityPlacer::new(inner, descending_index);
491 let placements = sorted.get_placements(&director);
492
493 assert_eq!(placements.len(), 3);
494 assert_eq!(placements[0].entity_ref.entity_index, 2);
495 assert_eq!(placements[1].entity_ref.entity_index, 1);
496 assert_eq!(placements[2].entity_ref.entity_index, 0);
497 }
498}