Skip to main content

solverforge_solver/heuristic/selector/
dynamic_scalar_nearby_change.rs

1//! Public dynamic nearby-change 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::{DynamicScalarChangeMove, MoveArena};
10
11use super::move_selector::{MoveSelector, MoveStreamContext};
12use super::scalar_neighborhood::{
13    dynamic_slot, emit_dynamic_scalar_change_move, RuntimeScalarFacadeCursor,
14    ScalarNeighborhoodBindingError, ScalarNeighborhoodLeaf, ScalarNeighborhoodSpec,
15};
16
17/// Direct public facade for one declared dynamic nearby-value source.
18///
19/// Construction is fallible by design. A missing structural nearby source is
20/// a binding error, not permission to substitute ordinary candidates.
21pub struct DynamicScalarNearbyChangeMoveSelector<S> {
22    leaf: ScalarNeighborhoodLeaf<S>,
23}
24
25pub type DynamicScalarNearbyChangeMoveCursor<S> =
26    RuntimeScalarFacadeCursor<S, DynamicScalarChangeMove<S>>;
27
28impl<S> DynamicScalarNearbyChangeMoveSelector<S>
29where
30    S: PlanningSolution,
31{
32    pub fn new(
33        slot: DynamicScalarVariableSlot<S>,
34        max_nearby: usize,
35        value_candidate_limit: Option<usize>,
36    ) -> Result<Self, ScalarNeighborhoodBindingError> {
37        Ok(Self {
38            leaf: ScalarNeighborhoodLeaf::from_spec(
39                ScalarNeighborhoodSpec::NearbyChange {
40                    max_nearby,
41                    value_candidate_limit,
42                },
43                dynamic_slot(slot),
44                None,
45            )?,
46        })
47    }
48}
49
50impl<S> Debug for DynamicScalarNearbyChangeMoveSelector<S>
51where
52    S: PlanningSolution,
53{
54    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
55        formatter
56            .debug_struct("DynamicScalarNearbyChangeMoveSelector")
57            .field("leaf", &self.leaf)
58            .finish()
59    }
60}
61
62impl<S> MoveSelector<S, DynamicScalarChangeMove<S>> for DynamicScalarNearbyChangeMoveSelector<S>
63where
64    S: PlanningSolution,
65    S::Score: Score,
66{
67    type Cursor<'a>
68        = DynamicScalarNearbyChangeMoveCursor<S>
69    where
70        Self: 'a;
71
72    fn open_cursor<'a, D: Director<S>>(&'a self, director: &D) -> Self::Cursor<'a> {
73        self.open_cursor_with_context(director, MoveStreamContext::default())
74    }
75
76    fn open_cursor_with_context<'a, D: Director<S>>(
77        &'a self,
78        director: &D,
79        context: MoveStreamContext,
80    ) -> Self::Cursor<'a> {
81        let mut stream_state = self.leaf.new_stream_state();
82        RuntimeScalarFacadeCursor::new(
83            self.leaf
84                .open_cursor_with_stream_state(&mut stream_state, director, context),
85            emit_dynamic_scalar_change_move::<S>,
86        )
87    }
88
89    fn size<D: Director<S>>(&self, director: &D) -> usize {
90        self.open_cursor(director).count()
91    }
92
93    fn append_moves<D: Director<S>>(
94        &self,
95        director: &D,
96        arena: &mut MoveArena<DynamicScalarChangeMove<S>>,
97    ) {
98        arena.extend(self.open_cursor(director));
99    }
100}