Skip to main content

solverforge_solver/builder/
basic_selector.rs

1//! Basic variable move selector enum and builder.
2
3use std::fmt::Debug;
4
5use solverforge_config::MoveSelectorConfig;
6use solverforge_core::domain::PlanningSolution;
7use solverforge_scoring::Director;
8
9use crate::heuristic::r#move::EitherMove;
10use crate::heuristic::selector::decorator::VecUnionSelector;
11use crate::heuristic::selector::typed_move_selector::MoveSelector;
12use crate::heuristic::selector::{
13    EitherChangeMoveSelector, EitherSwapMoveSelector, FromSolutionEntitySelector,
14};
15
16use super::context::BasicContext;
17
18/// A monomorphized leaf selector for basic (non-list) planning variables.
19///
20/// Wraps either a change or swap move selector in a uniform enum so that
21/// `VecUnionSelector<S, EitherMove<S, usize>, BasicLeafSelector<S>>` has a
22/// single concrete type regardless of which selectors are active.
23pub enum BasicLeafSelector<S: PlanningSolution> {
24    /// A change move selector yielding `EitherMove::Change`.
25    Change(
26        EitherChangeMoveSelector<
27            S,
28            usize,
29            FromSolutionEntitySelector,
30            crate::heuristic::selector::typed_value::StaticTypedValueSelector<S, usize>,
31        >,
32    ),
33    /// A swap move selector yielding `EitherMove::Swap`.
34    Swap(EitherSwapMoveSelector<S, usize, FromSolutionEntitySelector, FromSolutionEntitySelector>),
35}
36
37impl<S: PlanningSolution> Debug for BasicLeafSelector<S> {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            Self::Change(s) => write!(f, "BasicLeafSelector::Change({s:?})"),
41            Self::Swap(s) => write!(f, "BasicLeafSelector::Swap({s:?})"),
42        }
43    }
44}
45
46impl<S> MoveSelector<S, EitherMove<S, usize>> for BasicLeafSelector<S>
47where
48    S: PlanningSolution,
49{
50    fn iter_moves<'a, D: Director<S>>(
51        &'a self,
52        score_director: &'a D,
53    ) -> impl Iterator<Item = EitherMove<S, usize>> + 'a {
54        match self {
55            Self::Change(s) => {
56                let moves: Vec<_> = s.iter_moves(score_director).collect();
57                moves.into_iter()
58            }
59            Self::Swap(s) => {
60                let moves: Vec<_> = s.iter_moves(score_director).collect();
61                moves.into_iter()
62            }
63        }
64    }
65
66    fn size<D: Director<S>>(&self, score_director: &D) -> usize {
67        match self {
68            Self::Change(s) => s.size(score_director),
69            Self::Swap(s) => s.size(score_director),
70        }
71    }
72}
73
74/// Builder that constructs a `VecUnionSelector` of `BasicLeafSelector` from config.
75pub struct BasicMoveSelectorBuilder;
76
77impl BasicMoveSelectorBuilder {
78    /// Builds a `VecUnionSelector` from the given move selector config and domain context.
79    ///
80    /// - `ChangeMoveSelector` → `BasicLeafSelector::Change`
81    /// - `SwapMoveSelector` → `BasicLeafSelector::Swap`
82    /// - `UnionMoveSelector` → flattens children recursively
83    /// - `None` → default: Change + Swap
84    pub fn build<S>(
85        config: Option<&MoveSelectorConfig>,
86        ctx: &BasicContext<S>,
87    ) -> VecUnionSelector<S, EitherMove<S, usize>, BasicLeafSelector<S>>
88    where
89        S: PlanningSolution,
90    {
91        let mut leaves: Vec<BasicLeafSelector<S>> = Vec::new();
92        match config {
93            None => {
94                Self::push_change(&mut leaves, ctx);
95                Self::push_swap(&mut leaves, ctx);
96            }
97            Some(cfg) => Self::collect_leaves(cfg, ctx, &mut leaves),
98        }
99        VecUnionSelector::new(leaves)
100    }
101
102    fn collect_leaves<S>(
103        config: &MoveSelectorConfig,
104        ctx: &BasicContext<S>,
105        out: &mut Vec<BasicLeafSelector<S>>,
106    ) where
107        S: PlanningSolution,
108    {
109        match config {
110            MoveSelectorConfig::ChangeMoveSelector(_) => Self::push_change(out, ctx),
111            MoveSelectorConfig::SwapMoveSelector(_) => Self::push_swap(out, ctx),
112            MoveSelectorConfig::UnionMoveSelector(u) => {
113                for child in &u.selectors {
114                    Self::collect_leaves(child, ctx, out);
115                }
116            }
117            // All other variants are list selectors — ignore for basic solver
118            _ => {
119                // Default to change + swap if unknown selector type is specified
120                Self::push_change(out, ctx);
121                Self::push_swap(out, ctx);
122            }
123        }
124    }
125
126    fn push_change<S>(out: &mut Vec<BasicLeafSelector<S>>, ctx: &BasicContext<S>)
127    where
128        S: PlanningSolution,
129    {
130        out.push(BasicLeafSelector::Change(EitherChangeMoveSelector::simple(
131            ctx.get_variable,
132            ctx.set_variable,
133            ctx.descriptor_index,
134            ctx.variable_field,
135            ctx.values.clone(),
136        )));
137    }
138
139    fn push_swap<S>(out: &mut Vec<BasicLeafSelector<S>>, ctx: &BasicContext<S>)
140    where
141        S: PlanningSolution,
142    {
143        out.push(BasicLeafSelector::Swap(EitherSwapMoveSelector::simple(
144            ctx.get_variable,
145            ctx.set_variable,
146            ctx.descriptor_index,
147            ctx.variable_field,
148        )));
149    }
150}