Skip to main content

solverforge_solver/heuristic/selector/
dynamic_scalar_nearby_swap.rs

1//! Public dynamic nearby-swap facade over the canonical scalar leaf.
2
3use std::fmt::{self, Debug};
4
5use solverforge_core::domain::{DynamicScalarVariableSlot, PlanningSolution};
6use solverforge_core::score::Score;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::{DynamicScalarSwapMove, MoveArena};
10
11use super::move_selector::{MoveSelector, MoveStreamContext};
12use super::scalar_neighborhood::{
13    dynamic_slot, emit_dynamic_scalar_swap_move, RuntimeScalarFacadeCursor,
14    ScalarNeighborhoodBindingError, ScalarNeighborhoodLeaf, ScalarNeighborhoodSpec,
15};
16
17/// Direct public facade for one declared dynamic nearby-entity source.
18///
19/// Missing source metadata is a structural binding failure. In particular, it
20/// never changes the candidate universe to all entity pairs.
21pub struct DynamicScalarNearbySwapMoveSelector<S> {
22    leaf: ScalarNeighborhoodLeaf<S>,
23}
24
25pub type DynamicScalarNearbySwapMoveCursor<S> =
26    RuntimeScalarFacadeCursor<S, DynamicScalarSwapMove<S>>;
27
28impl<S> DynamicScalarNearbySwapMoveSelector<S>
29where
30    S: PlanningSolution,
31{
32    pub fn new(
33        slot: DynamicScalarVariableSlot<S>,
34        max_nearby: usize,
35    ) -> Result<Self, ScalarNeighborhoodBindingError> {
36        Ok(Self {
37            leaf: ScalarNeighborhoodLeaf::from_spec(
38                ScalarNeighborhoodSpec::NearbySwap { max_nearby },
39                dynamic_slot(slot),
40                None,
41            )?,
42        })
43    }
44}
45
46impl<S> Debug for DynamicScalarNearbySwapMoveSelector<S>
47where
48    S: PlanningSolution,
49{
50    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
51        formatter
52            .debug_struct("DynamicScalarNearbySwapMoveSelector")
53            .field("leaf", &self.leaf)
54            .finish()
55    }
56}
57
58impl<S> MoveSelector<S, DynamicScalarSwapMove<S>> for DynamicScalarNearbySwapMoveSelector<S>
59where
60    S: PlanningSolution,
61    S::Score: Score,
62{
63    type Cursor<'a>
64        = DynamicScalarNearbySwapMoveCursor<S>
65    where
66        Self: 'a;
67
68    fn open_cursor<'a, D: Director<S>>(&'a self, director: &D) -> Self::Cursor<'a> {
69        self.open_cursor_with_context(director, MoveStreamContext::default())
70    }
71
72    fn open_cursor_with_context<'a, D: Director<S>>(
73        &'a self,
74        director: &D,
75        context: MoveStreamContext,
76    ) -> Self::Cursor<'a> {
77        let mut stream_state = self.leaf.new_stream_state();
78        RuntimeScalarFacadeCursor::new(
79            self.leaf
80                .open_cursor_with_stream_state(&mut stream_state, director, context),
81            emit_dynamic_scalar_swap_move::<S>,
82        )
83    }
84
85    fn size<D: Director<S>>(&self, director: &D) -> usize {
86        self.open_cursor(director).count()
87    }
88
89    fn append_moves<D: Director<S>>(
90        &self,
91        director: &D,
92        arena: &mut MoveArena<DynamicScalarSwapMove<S>>,
93    ) {
94        arena.extend(self.open_cursor(director));
95    }
96}